mobility 0.5.1 → 0.6.0
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 +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
|