mobility 0.5.1 → 0.6.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -2
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +41 -1
- data/Gemfile.lock +3 -58
- data/README.md +22 -21
- data/lib/mobility.rb +3 -2
- data/lib/mobility/accumulator.rb +1 -2
- data/lib/mobility/active_model/backend_resetter.rb +1 -1
- data/lib/mobility/active_record.rb +12 -9
- data/lib/mobility/active_record/backend_resetter.rb +6 -7
- data/lib/mobility/active_record/uniqueness_validator.rb +12 -2
- data/lib/mobility/adapter.rb +1 -0
- data/lib/mobility/attributes.rb +3 -13
- data/lib/mobility/backends/active_record/column.rb +1 -0
- data/lib/mobility/backends/active_record/column/query_methods.rb +25 -20
- data/lib/mobility/backends/active_record/container/json_query_methods.rb +22 -16
- data/lib/mobility/backends/active_record/container/jsonb_query_methods.rb +19 -19
- data/lib/mobility/backends/active_record/hstore.rb +14 -12
- data/lib/mobility/backends/active_record/hstore/query_methods.rb +14 -5
- data/lib/mobility/backends/active_record/json.rb +21 -19
- data/lib/mobility/backends/active_record/json/query_methods.rb +16 -11
- data/lib/mobility/backends/active_record/jsonb.rb +21 -19
- data/lib/mobility/backends/active_record/jsonb/query_methods.rb +14 -5
- data/lib/mobility/backends/active_record/key_value.rb +9 -9
- data/lib/mobility/backends/active_record/key_value/query_methods.rb +53 -46
- data/lib/mobility/backends/active_record/pg_hash.rb +29 -25
- data/lib/mobility/backends/active_record/pg_query_methods.rb +76 -40
- data/lib/mobility/backends/active_record/query_methods.rb +17 -10
- data/lib/mobility/backends/active_record/serialized.rb +4 -2
- data/lib/mobility/backends/active_record/serialized/query_methods.rb +18 -15
- data/lib/mobility/backends/active_record/table.rb +21 -12
- data/lib/mobility/backends/active_record/table/query_methods.rb +82 -83
- data/lib/mobility/backends/hash_valued.rb +19 -0
- data/lib/mobility/backends/hstore.rb +3 -1
- data/lib/mobility/backends/json.rb +3 -1
- data/lib/mobility/backends/jsonb.rb +3 -1
- data/lib/mobility/backends/key_value.rb +32 -15
- data/lib/mobility/backends/sequel/column/query_methods.rb +16 -12
- data/lib/mobility/backends/sequel/container/json_query_methods.rb +25 -18
- data/lib/mobility/backends/sequel/container/jsonb_query_methods.rb +25 -18
- data/lib/mobility/backends/sequel/hstore.rb +14 -12
- data/lib/mobility/backends/sequel/hstore/query_methods.rb +18 -11
- data/lib/mobility/backends/sequel/json.rb +21 -19
- data/lib/mobility/backends/sequel/json/query_methods.rb +18 -11
- data/lib/mobility/backends/sequel/jsonb.rb +21 -19
- data/lib/mobility/backends/sequel/jsonb/query_methods.rb +18 -11
- data/lib/mobility/backends/sequel/key_value.rb +10 -11
- data/lib/mobility/backends/sequel/key_value/query_methods.rb +39 -34
- data/lib/mobility/backends/sequel/pg_hash.rb +37 -25
- data/lib/mobility/backends/sequel/pg_query_methods.rb +45 -20
- data/lib/mobility/backends/sequel/query_methods.rb +5 -0
- data/lib/mobility/backends/sequel/serialized.rb +18 -13
- data/lib/mobility/backends/sequel/serialized/query_methods.rb +10 -7
- data/lib/mobility/backends/sequel/table.rb +1 -1
- data/lib/mobility/backends/sequel/table/query_methods.rb +40 -35
- data/lib/mobility/plugins/cache/translation_cacher.rb +15 -15
- data/lib/mobility/plugins/default.rb +0 -7
- data/lib/mobility/plugins/fallbacks.rb +4 -0
- data/lib/mobility/sequel.rb +11 -5
- data/lib/mobility/sequel/backend_resetter.rb +6 -7
- data/lib/mobility/sequel/column_changes.rb +4 -4
- data/lib/mobility/version.rb +1 -1
- data/lib/rails/generators/mobility/backend_generators/base.rb +4 -0
- data/lib/rails/generators/mobility/backend_generators/table_backend.rb +0 -12
- data/lib/rails/generators/mobility/templates/column_translations.rb +2 -2
- data/lib/rails/generators/mobility/templates/create_string_translations.rb +5 -5
- data/lib/rails/generators/mobility/templates/create_text_translations.rb +5 -5
- data/lib/rails/generators/mobility/templates/initializer.rb +8 -0
- data/lib/rails/generators/mobility/templates/table_migration.rb +2 -3
- data/lib/rails/generators/mobility/templates/table_translations.rb +3 -4
- data/lib/rails/generators/mobility/translations_generator.rb +6 -5
- metadata +2 -3
- metadata.gz.sig +0 -0
- data/lib/mobility/backend/stringify_locale.rb +0 -18
@@ -3,51 +3,56 @@ require "mobility/backends/sequel/query_methods"
|
|
3
3
|
|
4
4
|
module Mobility
|
5
5
|
module Backends
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
module Sequel
|
7
|
+
class KeyValue::QueryMethods < QueryMethods
|
8
|
+
def initialize(attributes, association_name: nil, class_name: nil, **)
|
9
|
+
super
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
define_join_method(association_name, class_name)
|
12
|
+
define_query_methods(association_name)
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
+
private
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
17
|
+
def define_join_method(association_name, translation_class)
|
18
|
+
define_method :"join_#{association_name}" do |*attributes, **options|
|
19
|
+
attributes.inject(self) do |relation, attribute|
|
20
|
+
join_type = options[:outer_join] ? :left_outer : :inner
|
21
|
+
relation.join_table(join_type,
|
22
|
+
translation_class.table_name,
|
23
|
+
{
|
24
|
+
key: attribute.to_s,
|
25
|
+
locale: Mobility.locale.to_s,
|
26
|
+
translatable_type: model.name,
|
27
|
+
translatable_id: ::Sequel[:"#{model.table_name}"][:id]
|
28
|
+
},
|
29
|
+
table_alias: "#{attribute}_#{association_name}")
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
31
|
-
end
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
def define_query_methods(association_name)
|
35
|
+
q = self
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
%w[exclude or where].each do |method_name|
|
38
|
+
define_method method_name do |*conds, &block|
|
39
|
+
if i18n_keys = q.extract_attributes(conds.first)
|
40
|
+
cond = conds.first.dup
|
41
|
+
i18n_nulls = i18n_keys.select { |key| cond[key].nil? }
|
42
|
+
i18n_keys.each do |attr|
|
43
|
+
cond[::Sequel[:"#{attr}_#{association_name}"][:value]] = q.collapse cond.delete(attr)
|
44
|
+
end
|
45
|
+
super(cond, &block).
|
46
|
+
send("join_#{association_name}", *(i18n_keys - i18n_nulls), outer_join: method_name == "or").
|
47
|
+
send("join_#{association_name}", *i18n_nulls, outer_join: true)
|
48
|
+
else
|
49
|
+
super(*conds, &block)
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
55
|
+
KeyValue.private_constant :QueryMethods
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
require "mobility/util"
|
3
3
|
require "mobility/backends/sequel"
|
4
4
|
require "mobility/backends/hash_valued"
|
5
|
-
require "mobility/backend/stringify_locale"
|
6
5
|
require "mobility/sequel/column_changes"
|
7
6
|
|
8
7
|
module Mobility
|
@@ -13,36 +12,49 @@ Internal class used by Sequel backends backed by a Postgres data type (hstore,
|
|
13
12
|
jsonb).
|
14
13
|
|
15
14
|
=end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# @!macro backend_iterator
|
22
|
-
def each_locale
|
23
|
-
super { |l| yield l.to_sym }
|
24
|
-
end
|
15
|
+
module Sequel
|
16
|
+
class PgHash
|
17
|
+
include Sequel
|
18
|
+
include HashValued
|
25
19
|
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
def read(locale, options = {})
|
21
|
+
super(locale.to_s, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(locale, value, options = {})
|
25
|
+
super(locale.to_s, value, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @!macro backend_iterator
|
29
|
+
def each_locale
|
30
|
+
super { |l| yield l.to_sym }
|
31
|
+
end
|
32
|
+
|
33
|
+
def translations
|
34
|
+
model[column_name.to_sym]
|
35
|
+
end
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
setup do |attributes, options|
|
38
|
+
column_affix = "#{options[:column_prefix]}%s#{options[:column_suffix]}"
|
39
|
+
columns = attributes.map { |attribute| (column_affix % attribute).to_sym }
|
40
|
+
|
41
|
+
before_validation = Module.new do
|
42
|
+
define_method :before_validation do
|
43
|
+
columns.each do |column|
|
44
|
+
self[column].delete_if { |_, v| Util.blank?(v) }
|
45
|
+
end
|
46
|
+
super()
|
35
47
|
end
|
36
|
-
super()
|
37
48
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
include Mobility::Sequel::ColumnChanges.new(*attributes)
|
49
|
+
include before_validation
|
50
|
+
include Mobility::Sequel::HashInitializer.new(*columns)
|
51
|
+
include Mobility::Sequel::ColumnChanges.new(attributes, column_affix: column_affix)
|
42
52
|
|
43
|
-
|
44
|
-
|
53
|
+
plugin :defaults_setter
|
54
|
+
columns.each { |column| default_values[column] = {} }
|
55
|
+
end
|
45
56
|
end
|
57
|
+
private_constant :PgHash
|
46
58
|
end
|
47
59
|
end
|
48
60
|
end
|
@@ -1,25 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Mobility
|
2
4
|
module Backends
|
3
5
|
module Sequel
|
4
6
|
=begin
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
+
Internal module builder defining query methods for Postgres backends. Including
|
9
|
+
class must define three methods:
|
8
10
|
|
9
|
-
- a
|
10
|
-
|
11
|
-
|
12
|
-
- a
|
13
|
-
|
14
|
-
|
11
|
+
- a method +matches+ which takes a key (column name) and a locale to match and
|
12
|
+
returns an SQL expression checking that the column has the specified value
|
13
|
+
in the specified locale
|
14
|
+
- a method +exists+ which takes a key (column name) and locale, and returns
|
15
|
+
an SQL expression which checks that the column has a value in the locale
|
16
|
+
- a method +quote+ which quotes the value to be matched
|
15
17
|
|
16
|
-
(The +matches+
|
18
|
+
(The +matches+/+exists+/+quote+ methods are implemented slightly differently
|
17
19
|
for hstore/json/jsonb/container backends.)
|
18
20
|
|
21
|
+
@see Mobility::Backends::Sequel::Json::QueryMethods
|
22
|
+
@see Mobility::Backends::Sequel::Jsonb::QueryMethods
|
23
|
+
@see Mobility::Backends::Sequel::Hstore::QueryMethods
|
24
|
+
@see Mobility::Backends::Sequel::Container::JsonQueryMethods
|
25
|
+
@see Mobility::Backends::Sequel::Container::JsonbQueryMethods
|
26
|
+
|
19
27
|
=end
|
20
28
|
module PgQueryMethods
|
21
|
-
|
29
|
+
attr_reader :column_affix
|
30
|
+
|
31
|
+
def initialize(attributes, options)
|
22
32
|
super
|
33
|
+
@column_affix = "#{options[:column_prefix]}%s#{options[:column_suffix]}"
|
23
34
|
define_query_methods
|
24
35
|
end
|
25
36
|
|
@@ -33,11 +44,23 @@ for hstore/json/jsonb/container backends.)
|
|
33
44
|
def create_query!(cond, keys, invert = false)
|
34
45
|
keys.map { |key|
|
35
46
|
values = cond.delete(key)
|
36
|
-
values =
|
37
|
-
|
47
|
+
values = values.is_a?(Array) ? values.uniq: [values]
|
48
|
+
create_query_op(key, values, invert)
|
38
49
|
}.inject(invert ? :| : :&)
|
39
50
|
end
|
40
51
|
|
52
|
+
def matches(_key, _locale)
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
def exists(_key, _locale)
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
def quote(_value)
|
61
|
+
raise NotImplementedError
|
62
|
+
end
|
63
|
+
|
41
64
|
private
|
42
65
|
|
43
66
|
def define_query_methods
|
@@ -66,24 +89,26 @@ for hstore/json/jsonb/container backends.)
|
|
66
89
|
end
|
67
90
|
end
|
68
91
|
|
69
|
-
def create_query_op(key,
|
92
|
+
def create_query_op(key, values, invert)
|
70
93
|
locale = Mobility.locale.to_s
|
94
|
+
values = values.map(&method(:quote))
|
95
|
+
values = values.first if values.size == 1
|
96
|
+
|
97
|
+
match = matches(key, locale) =~ values
|
71
98
|
|
72
99
|
if invert
|
73
|
-
|
100
|
+
exists(key, locale) & ~match
|
74
101
|
else
|
75
|
-
|
102
|
+
values.nil? ? ~exists(key, locale) : match
|
76
103
|
end
|
77
104
|
end
|
78
105
|
|
79
|
-
def matches(_key, _value, _locale)
|
80
|
-
raise NotImplementedError
|
81
|
-
end
|
82
106
|
|
83
|
-
def
|
84
|
-
|
107
|
+
def column_name(attribute)
|
108
|
+
(column_affix % attribute).to_sym
|
85
109
|
end
|
86
110
|
end
|
111
|
+
private_constant :PgQueryMethods
|
87
112
|
end
|
88
113
|
end
|
89
114
|
end
|
@@ -25,7 +25,12 @@ models. For details see backend-specific subclasses.
|
|
25
25
|
def extract_attributes(cond)
|
26
26
|
cond.is_a?(Hash) && Util.presence(cond.keys & @attributes)
|
27
27
|
end
|
28
|
+
|
29
|
+
def collapse(value)
|
30
|
+
value.is_a?(Array) ? value.uniq : value
|
31
|
+
end
|
28
32
|
end
|
33
|
+
private_constant :QueryMethods
|
29
34
|
end
|
30
35
|
end
|
31
36
|
end
|
@@ -49,18 +49,20 @@ Sequel serialization plugin.
|
|
49
49
|
|
50
50
|
setup do |attributes, options|
|
51
51
|
format = options[:format]
|
52
|
+
column_affix = "#{options[:column_prefix]}%s#{options[:column_suffix]}"
|
53
|
+
columns = attributes.map { |attribute| (column_affix % attribute).to_sym }
|
54
|
+
|
52
55
|
plugin :serialization
|
53
56
|
plugin :serialization_modification_detection
|
54
57
|
|
55
|
-
|
56
|
-
|
57
|
-
self.
|
58
|
-
self.deserialization_map[attribute] = Serialized.deserializer_for(format)
|
58
|
+
columns.each do |column|
|
59
|
+
self.serialization_map[column] = Serialized.serializer_for(format)
|
60
|
+
self.deserialization_map[column] = Serialized.deserializer_for(format)
|
59
61
|
end
|
60
62
|
|
61
63
|
method_overrides = Module.new do
|
62
64
|
define_method :initialize_set do |values|
|
63
|
-
|
65
|
+
columns.each { |column| self[column] = {}.send(:"to_#{format}") }
|
64
66
|
super(values)
|
65
67
|
end
|
66
68
|
end
|
@@ -74,13 +76,12 @@ Sequel serialization plugin.
|
|
74
76
|
# Returns deserialized column value
|
75
77
|
# @return [Hash]
|
76
78
|
def translations
|
77
|
-
|
78
|
-
|
79
|
-
model.deserialized_values[attribute_]
|
79
|
+
if model.deserialized_values.has_key?(column_name)
|
80
|
+
model.deserialized_values[column_name]
|
80
81
|
elsif model.frozen?
|
81
|
-
deserialize_value(
|
82
|
+
deserialize_value(serialized_value)
|
82
83
|
else
|
83
|
-
model.deserialized_values[
|
84
|
+
model.deserialized_values[column_name] = deserialize_value(serialized_value)
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
@@ -96,12 +97,16 @@ Sequel serialization plugin.
|
|
96
97
|
|
97
98
|
private
|
98
99
|
|
99
|
-
def deserialize_value(
|
100
|
-
model.send(:deserialize_value,
|
100
|
+
def deserialize_value(value)
|
101
|
+
model.send(:deserialize_value, column_name, value)
|
101
102
|
end
|
102
103
|
|
103
104
|
def serialized_value
|
104
|
-
model[
|
105
|
+
model[column_name]
|
106
|
+
end
|
107
|
+
|
108
|
+
def column_name
|
109
|
+
super.to_sym
|
105
110
|
end
|
106
111
|
end
|
107
112
|
end
|
@@ -3,17 +3,20 @@ require "mobility/backends/sequel/query_methods"
|
|
3
3
|
|
4
4
|
module Mobility
|
5
5
|
module Backends
|
6
|
-
|
7
|
-
|
6
|
+
module Sequel
|
7
|
+
class Serialized::QueryMethods < QueryMethods
|
8
|
+
include Backends::Serialized
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def initialize(attributes, _)
|
11
|
+
super
|
12
|
+
q = self
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
define_method :where do |*cond, &block|
|
15
|
+
q.check_opts(cond.first) || super(*cond, &block)
|
16
|
+
end
|
15
17
|
end
|
16
18
|
end
|
19
|
+
Serialized.private_constant :QueryMethods
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -84,7 +84,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
84
84
|
end
|
85
85
|
include callback_methods
|
86
86
|
|
87
|
-
include Mobility::Sequel::ColumnChanges.new(
|
87
|
+
include Mobility::Sequel::ColumnChanges.new(attributes)
|
88
88
|
end
|
89
89
|
|
90
90
|
setup_query_methods(QueryMethods)
|
@@ -3,51 +3,56 @@ require "mobility/backends/sequel/query_methods"
|
|
3
3
|
|
4
4
|
module Mobility
|
5
5
|
module Backends
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
module Sequel
|
7
|
+
class Table::QueryMethods < QueryMethods
|
8
|
+
def initialize(attributes, association_name: nil, model_class: nil, subclass_name: nil, **options)
|
9
|
+
super
|
10
|
+
translation_class = model_class.const_get(subclass_name)
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
define_join_method(association_name, translation_class, **options)
|
13
|
+
define_query_methods(association_name, translation_class, **options)
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def define_join_method(association_name, translation_class, table_name: nil, foreign_key: nil, **)
|
17
|
+
define_method :"join_#{association_name}" do |**options|
|
18
|
+
if joins = @opts[:join]
|
19
|
+
# Return self if we've already joined this table
|
20
|
+
return self if joins.any? { |clause| clause.table_expr == table_name }
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
join_type = options[:outer_join] ? :left_outer : :inner
|
24
|
+
join_table(join_type,
|
25
|
+
translation_class.table_name,
|
26
|
+
{
|
27
|
+
locale: Mobility.locale.to_s,
|
28
|
+
foreign_key => ::Sequel[model.table_name][:id]
|
29
|
+
})
|
30
|
+
end
|
29
31
|
end
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
def define_query_methods(association_name, translation_class, **)
|
34
|
+
q = self
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
36
|
+
# See note in AR Table QueryMethods class about limitations of
|
37
|
+
# query methods on translated attributes when searching on nil values.
|
38
|
+
#
|
39
|
+
%w[exclude or where].each do |method_name|
|
40
|
+
define_method method_name do |*conds, &block|
|
41
|
+
if i18n_keys = q.extract_attributes(conds.first)
|
42
|
+
cond = conds.first.dup
|
43
|
+
outer_join = method_name == "or" || i18n_keys.all? { |key| cond[key].nil? }
|
44
|
+
i18n_keys.each do |attr|
|
45
|
+
cond[::Sequel[translation_class.table_name][attr]] = q.collapse cond.delete(attr)
|
46
|
+
end
|
47
|
+
super(cond, &block).send("join_#{association_name}", outer_join: outer_join)
|
48
|
+
else
|
49
|
+
super(*conds, &block)
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
55
|
+
Table.private_constant :QueryMethods
|
51
56
|
end
|
52
57
|
end
|
53
58
|
end
|