mobility 0.8.8 → 1.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +52 -16
- data/Gemfile.lock +113 -52
- data/Guardfile +23 -1
- data/README.md +184 -92
- data/Rakefile +6 -4
- data/lib/mobility.rb +40 -166
- data/lib/mobility/active_record/translation.rb +1 -1
- data/lib/mobility/arel/nodes/pg_ops.rb +1 -1
- data/lib/mobility/backend.rb +19 -41
- data/lib/mobility/backends.rb +20 -0
- data/lib/mobility/backends/active_record.rb +4 -0
- data/lib/mobility/backends/active_record/column.rb +2 -0
- data/lib/mobility/backends/active_record/container.rb +4 -2
- data/lib/mobility/backends/active_record/hstore.rb +2 -0
- data/lib/mobility/backends/active_record/json.rb +2 -0
- data/lib/mobility/backends/active_record/jsonb.rb +2 -0
- data/lib/mobility/backends/active_record/key_value.rb +5 -3
- data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
- data/lib/mobility/backends/active_record/serialized.rb +2 -0
- data/lib/mobility/backends/active_record/table.rb +5 -3
- data/lib/mobility/backends/column.rb +0 -6
- data/lib/mobility/backends/container.rb +2 -1
- data/lib/mobility/backends/hash.rb +39 -0
- data/lib/mobility/backends/hstore.rb +0 -1
- data/lib/mobility/backends/json.rb +0 -1
- data/lib/mobility/backends/jsonb.rb +0 -1
- data/lib/mobility/backends/key_value.rb +22 -14
- data/lib/mobility/backends/null.rb +2 -0
- data/lib/mobility/backends/sequel.rb +3 -0
- data/lib/mobility/backends/sequel/column.rb +2 -0
- data/lib/mobility/backends/sequel/container.rb +3 -1
- data/lib/mobility/backends/sequel/hstore.rb +2 -0
- data/lib/mobility/backends/sequel/json.rb +2 -0
- data/lib/mobility/backends/sequel/jsonb.rb +3 -1
- data/lib/mobility/backends/sequel/key_value.rb +8 -6
- data/lib/mobility/backends/sequel/serialized.rb +2 -0
- data/lib/mobility/backends/sequel/table.rb +5 -2
- data/lib/mobility/backends/serialized.rb +1 -3
- data/lib/mobility/backends/table.rb +14 -6
- data/lib/mobility/pluggable.rb +36 -0
- data/lib/mobility/plugin.rb +260 -0
- data/lib/mobility/plugins.rb +26 -25
- data/lib/mobility/plugins/active_model.rb +17 -0
- data/lib/mobility/plugins/active_model/cache.rb +26 -0
- data/lib/mobility/plugins/active_model/dirty.rb +310 -54
- data/lib/mobility/plugins/active_record.rb +34 -0
- data/lib/mobility/plugins/active_record/backend.rb +25 -0
- data/lib/mobility/plugins/active_record/cache.rb +28 -0
- data/lib/mobility/plugins/active_record/dirty.rb +72 -101
- data/lib/mobility/plugins/active_record/query.rb +48 -34
- data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
- data/lib/mobility/plugins/attribute_methods.rb +28 -20
- data/lib/mobility/plugins/attributes.rb +70 -0
- data/lib/mobility/plugins/backend.rb +138 -0
- data/lib/mobility/plugins/backend_reader.rb +34 -0
- data/lib/mobility/plugins/cache.rb +59 -24
- data/lib/mobility/plugins/default.rb +22 -17
- data/lib/mobility/plugins/dirty.rb +12 -33
- data/lib/mobility/plugins/fallbacks.rb +51 -43
- data/lib/mobility/plugins/fallthrough_accessors.rb +26 -25
- data/lib/mobility/plugins/locale_accessors.rb +25 -35
- data/lib/mobility/plugins/presence.rb +28 -21
- data/lib/mobility/plugins/query.rb +8 -17
- data/lib/mobility/plugins/reader.rb +50 -0
- data/lib/mobility/plugins/sequel.rb +34 -0
- data/lib/mobility/plugins/sequel/backend.rb +25 -0
- data/lib/mobility/plugins/sequel/cache.rb +24 -0
- data/lib/mobility/plugins/sequel/dirty.rb +45 -32
- data/lib/mobility/plugins/sequel/query.rb +21 -6
- data/lib/mobility/plugins/writer.rb +44 -0
- data/lib/mobility/translations.rb +95 -0
- data/lib/mobility/version.rb +12 -1
- data/lib/rails/generators/mobility/templates/initializer.rb +95 -77
- metadata +51 -51
- metadata.gz.sig +0 -0
- data/lib/mobility/active_model.rb +0 -4
- data/lib/mobility/active_model/backend_resetter.rb +0 -26
- data/lib/mobility/active_record.rb +0 -23
- data/lib/mobility/active_record/backend_resetter.rb +0 -26
- data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
- data/lib/mobility/attributes.rb +0 -324
- data/lib/mobility/backend/orm_delegator.rb +0 -44
- data/lib/mobility/backend_resetter.rb +0 -50
- data/lib/mobility/configuration.rb +0 -138
- data/lib/mobility/fallbacks.rb +0 -28
- data/lib/mobility/interface.rb +0 -0
- data/lib/mobility/loaded.rb +0 -4
- data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
- data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
- data/lib/mobility/sequel.rb +0 -9
- data/lib/mobility/sequel/backend_resetter.rb +0 -23
- data/lib/mobility/translates.rb +0 -73
@@ -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
|
-
|
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
|
@@ -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] = "#{
|
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(
|
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
|
@@ -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 =
|
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
|
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.
|
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
|
@@ -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
|
@@ -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]
|
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
|
-
|
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::
|
114
|
-
# @return (see Backend::
|
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
|
-
|
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
|
@@ -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],
|
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
|
@@ -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] = "#{
|
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(
|
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|
|
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
|