mobility 0.7.6 → 0.8.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 +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +14 -3
- data/Gemfile.lock +57 -0
- data/README.md +15 -4
- data/lib/mobility.rb +17 -1
- data/lib/mobility/active_record/uniqueness_validator.rb +1 -1
- data/lib/mobility/arel/nodes.rb +0 -3
- data/lib/mobility/arel/nodes/pg_ops.rb +0 -4
- data/lib/mobility/attributes.rb +42 -25
- data/lib/mobility/backends/active_record.rb +2 -2
- data/lib/mobility/backends/active_record/column.rb +2 -2
- data/lib/mobility/backends/active_record/key_value.rb +6 -9
- data/lib/mobility/backends/active_record/table.rb +6 -7
- data/lib/mobility/backends/column.rb +2 -2
- data/lib/mobility/backends/key_value.rb +5 -0
- data/lib/mobility/backends/sequel.rb +24 -11
- data/lib/mobility/backends/sequel/column.rb +4 -3
- data/lib/mobility/backends/sequel/container.rb +21 -12
- data/lib/mobility/backends/sequel/hstore.rb +11 -3
- data/lib/mobility/backends/sequel/json.rb +11 -3
- data/lib/mobility/backends/sequel/jsonb.rb +35 -3
- data/lib/mobility/backends/sequel/key_value.rb +97 -17
- data/lib/mobility/backends/sequel/serialized.rb +5 -4
- data/lib/mobility/backends/sequel/table.rb +95 -26
- data/lib/mobility/backends/table.rb +4 -0
- data/lib/mobility/configuration.rb +1 -1
- data/lib/mobility/plugins/active_record/query.rb +52 -26
- data/lib/mobility/plugins/locale_accessors.rb +3 -2
- data/lib/mobility/plugins/query.rb +3 -0
- data/lib/mobility/plugins/sequel/query.rb +140 -0
- data/lib/mobility/sequel.rb +0 -14
- data/lib/mobility/sequel/sql.rb +16 -0
- data/lib/mobility/version.rb +1 -1
- data/lib/rails/generators/mobility/templates/column_translations.rb +1 -1
- data/lib/rails/generators/mobility/templates/initializer.rb +3 -2
- data/lib/rails/generators/mobility/translations_generator.rb +1 -1
- metadata +27 -46
- metadata.gz.sig +1 -1
- data/lib/mobility/backends/active_record/query_methods.rb +0 -50
- data/lib/mobility/backends/sequel/column/query_methods.rb +0 -29
- data/lib/mobility/backends/sequel/container/json_query_methods.rb +0 -41
- data/lib/mobility/backends/sequel/container/jsonb_query_methods.rb +0 -41
- data/lib/mobility/backends/sequel/hstore/query_methods.rb +0 -34
- data/lib/mobility/backends/sequel/json/query_methods.rb +0 -34
- data/lib/mobility/backends/sequel/jsonb/query_methods.rb +0 -34
- data/lib/mobility/backends/sequel/key_value/query_methods.rb +0 -58
- data/lib/mobility/backends/sequel/pg_query_methods.rb +0 -114
- data/lib/mobility/backends/sequel/query_methods.rb +0 -36
- data/lib/mobility/backends/sequel/serialized/query_methods.rb +0 -22
- data/lib/mobility/backends/sequel/table/query_methods.rb +0 -58
@@ -36,8 +36,6 @@ Sequel serialization plugin.
|
|
36
36
|
include Sequel
|
37
37
|
include HashValued
|
38
38
|
|
39
|
-
require 'mobility/backends/sequel/serialized/query_methods'
|
40
|
-
|
41
39
|
# @!group Backend Configuration
|
42
40
|
# @param (see Backends::Serialized.configure)
|
43
41
|
# @option (see Backends::Serialized.configure)
|
@@ -48,6 +46,11 @@ Sequel serialization plugin.
|
|
48
46
|
end
|
49
47
|
# @!endgroup
|
50
48
|
|
49
|
+
def self.build_op(attr, _locale)
|
50
|
+
raise ArgumentError,
|
51
|
+
"You cannot query on mobility attributes translated with the Serialized backend (#{attr})."
|
52
|
+
end
|
53
|
+
|
51
54
|
setup do |attributes, options|
|
52
55
|
format = options[:format]
|
53
56
|
columns = attributes.map { |attribute| (options[:column_affix] % attribute).to_sym }
|
@@ -71,8 +74,6 @@ Sequel serialization plugin.
|
|
71
74
|
include SerializationModificationDetectionFix
|
72
75
|
end
|
73
76
|
|
74
|
-
setup_query_methods(QueryMethods)
|
75
|
-
|
76
77
|
# Returns deserialized column value
|
77
78
|
# @return [Hash]
|
78
79
|
def translations
|
@@ -3,6 +3,7 @@ require "mobility/util"
|
|
3
3
|
require "mobility/backends/sequel"
|
4
4
|
require "mobility/backends/key_value"
|
5
5
|
require "mobility/sequel/model_translation"
|
6
|
+
require "mobility/sequel/sql"
|
6
7
|
|
7
8
|
module Mobility
|
8
9
|
module Backends
|
@@ -15,37 +16,107 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
15
16
|
include Sequel
|
16
17
|
include Table
|
17
18
|
|
18
|
-
require 'mobility/backends/sequel/table/query_methods'
|
19
|
-
|
20
19
|
def translation_class
|
21
20
|
self.class.translation_class
|
22
21
|
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
class << self
|
24
|
+
# @return [Symbol] class for translations
|
25
|
+
def translation_class
|
26
|
+
@translation_class ||= model_class.const_get(subclass_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @!group Backend Configuration
|
30
|
+
# @option options [Symbol] association_name (:translations) Name of association method
|
31
|
+
# @option options [Symbol] table_name Name of translation table
|
32
|
+
# @option options [Symbol] foreign_key Name of foreign key
|
33
|
+
# @option options [Symbol] subclass_name Name of subclass to append to model class to generate translation class
|
34
|
+
# @raise [CacheRequired] if cache option is false
|
35
|
+
def configure(options)
|
36
|
+
raise CacheRequired, "Cache required for Sequel::Table backend" if options[:cache] == false
|
37
|
+
table_name = Util.singularize(options[:model_class].table_name)
|
38
|
+
options[:table_name] ||= :"#{table_name}_translations"
|
39
|
+
options[:foreign_key] ||= Util.foreign_key(Util.camelize(table_name.downcase))
|
40
|
+
if association_name = options[:association_name]
|
41
|
+
options[:subclass_name] ||= Util.camelize(Util.singularize(association_name))
|
42
|
+
else
|
43
|
+
options[:association_name] = :translations
|
44
|
+
options[:subclass_name] ||= :Translation
|
45
|
+
end
|
46
|
+
%i[table_name foreign_key association_name subclass_name].each { |key| options[key] = options[key].to_sym }
|
47
|
+
end
|
48
|
+
# @!endgroup
|
49
|
+
|
50
|
+
# @param [Symbol] name Attribute name
|
51
|
+
# @param [Symbol] locale Locale
|
52
|
+
# @return [Sequel::SQL::QualifiedIdentifier]
|
53
|
+
def build_op(attr, locale)
|
54
|
+
::Mobility::Sequel::SQL::QualifiedIdentifier.new(table_alias(locale), attr, locale, self, attribute_name: attr)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param [Sequel::Dataset] dataset Dataset to prepare
|
58
|
+
# @param [Object] predicate Predicate
|
59
|
+
# @param [Symbol] locale Locale
|
60
|
+
# @return [Sequel::Dataset] Prepared dataset
|
61
|
+
def prepare_dataset(dataset, predicate, locale)
|
62
|
+
join_translations(dataset, locale, visit(predicate, locale))
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def join_translations(dataset, locale, join_type)
|
68
|
+
if joins = dataset.opts[:join]
|
69
|
+
return dataset if joins.any? { |clause| clause.table_expr.alias == table_alias(locale) }
|
70
|
+
end
|
71
|
+
dataset.join_table(join_type,
|
72
|
+
translation_class.table_name,
|
73
|
+
{
|
74
|
+
locale: locale.to_s,
|
75
|
+
foreign_key => ::Sequel[model_class.table_name][:id]
|
76
|
+
},
|
77
|
+
table_alias: table_alias(locale))
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [Symbol] Join type
|
81
|
+
def visit(predicate, locale)
|
82
|
+
case predicate
|
83
|
+
when Array
|
84
|
+
visit_collection(predicate, locale)
|
85
|
+
when ::Mobility::Sequel::SQL::QualifiedIdentifier
|
86
|
+
visit_sql_identifier(predicate, locale)
|
87
|
+
when ::Sequel::SQL::BooleanExpression
|
88
|
+
visit_boolean(predicate, locale)
|
89
|
+
when ::Sequel::SQL::Expression
|
90
|
+
visit(predicate.args, locale)
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def visit_collection(collection, locale)
|
97
|
+
collection.map { |obj|
|
98
|
+
visit(obj, locale).tap do |visited|
|
99
|
+
return visited if visited == :inner
|
100
|
+
end
|
101
|
+
}.compact.first
|
102
|
+
end
|
28
103
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
options[:association_name] = :translations
|
44
|
-
options[:subclass_name] ||= :Translation
|
104
|
+
def visit_sql_identifier(identifier, locale)
|
105
|
+
(table_alias(locale) == identifier.table) && :inner
|
106
|
+
end
|
107
|
+
|
108
|
+
def visit_boolean(boolean, locale)
|
109
|
+
if boolean.op == :'='
|
110
|
+
boolean.args.any? { |op| visit(op, locale) } && :inner
|
111
|
+
elsif boolean.op == :IS
|
112
|
+
boolean.args.any?(&:nil?) && :left_outer
|
113
|
+
elsif boolean.op == :OR
|
114
|
+
boolean.args.any? { |op| visit(op, locale) } && :left_outer
|
115
|
+
else
|
116
|
+
visit(boolean.args, locale)
|
117
|
+
end
|
45
118
|
end
|
46
|
-
%i[table_name foreign_key association_name subclass_name].each { |key| options[key] = options[key].to_sym }
|
47
119
|
end
|
48
|
-
# @!endgroup
|
49
120
|
|
50
121
|
setup do |attributes, options|
|
51
122
|
association_name = options[:association_name]
|
@@ -86,8 +157,6 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
86
157
|
include Mobility::Sequel::ColumnChanges.new(attributes)
|
87
158
|
end
|
88
159
|
|
89
|
-
setup_query_methods(QueryMethods)
|
90
|
-
|
91
160
|
def translation_for(locale, _)
|
92
161
|
translation = model.send(association_name).find { |t| t.locale == locale.to_s }
|
93
162
|
translation ||= translation_class.new(locale: locale)
|
@@ -104,7 +104,7 @@ default_fallbacks= will be removed in the next major version of Mobility.
|
|
104
104
|
@accessor_method = :translates
|
105
105
|
@query_method = :i18n
|
106
106
|
@fallbacks_generator = lambda { |fallbacks| Mobility::Fallbacks.build(fallbacks) }
|
107
|
-
@default_accessor_locales = lambda {
|
107
|
+
@default_accessor_locales = lambda { Mobility.available_locales }
|
108
108
|
@default_options = Options[{
|
109
109
|
cache: true,
|
110
110
|
presence: true,
|
@@ -17,9 +17,7 @@ enabled for any one attribute on the model.
|
|
17
17
|
module Query
|
18
18
|
class << self
|
19
19
|
def apply(attributes)
|
20
|
-
|
21
|
-
|
22
|
-
model_class.class_eval do
|
20
|
+
attributes.model_class.class_eval do
|
23
21
|
extend QueryMethod
|
24
22
|
extend FindByMethods.new(*attributes.names)
|
25
23
|
singleton_class.send :alias_method, Mobility.query_method, :__mobility_query_scope__
|
@@ -48,7 +46,7 @@ enabled for any one attribute on the model.
|
|
48
46
|
end
|
49
47
|
|
50
48
|
def method_missing(m, *)
|
51
|
-
if @model_class.
|
49
|
+
if @model_class.mobility_attribute?(m)
|
52
50
|
@__backends |= [@model_class.mobility_backend_class(m)]
|
53
51
|
@model_class.mobility_backend_class(m).build_node(m, @locale)
|
54
52
|
elsif @model_class.column_names.include?(m.to_s)
|
@@ -91,6 +89,26 @@ enabled for any one attribute on the model.
|
|
91
89
|
opts == :chain ? WhereChain.new(spawn) : super
|
92
90
|
end
|
93
91
|
|
92
|
+
def order(opts, *rest)
|
93
|
+
case opts
|
94
|
+
when Symbol, String
|
95
|
+
@klass.mobility_attribute?(opts) ? order({ opts => :asc }, *rest) : super
|
96
|
+
when Hash
|
97
|
+
i18n_keys, keys = opts.keys.partition(&@klass.method(:mobility_attribute?))
|
98
|
+
return super if i18n_keys.empty?
|
99
|
+
|
100
|
+
base = keys.empty? ? self : super(opts.slice(keys))
|
101
|
+
|
102
|
+
i18n_keys.inject(base) do |query, key|
|
103
|
+
backend_class = @klass.mobility_backend_class(key)
|
104
|
+
dir, node = opts[key], backend_node(key)
|
105
|
+
backend_class.apply_scope(query, node).order(node.send(dir.downcase))
|
106
|
+
end
|
107
|
+
else
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
94
112
|
# Return backend node for attribute name.
|
95
113
|
# @param [Symbol,String] name Name of attribute
|
96
114
|
# @param [Symbol] locale Locale
|
@@ -108,38 +126,42 @@ enabled for any one attribute on the model.
|
|
108
126
|
end
|
109
127
|
|
110
128
|
module QueryBuilder
|
129
|
+
IDENTITY = ->(x) { x }.freeze
|
130
|
+
|
111
131
|
class << self
|
112
|
-
def build(scope, where_opts, invert: false)
|
132
|
+
def build(scope, where_opts, invert: false, &block)
|
113
133
|
return yield unless Hash === where_opts
|
114
134
|
|
115
135
|
opts = where_opts.with_indifferent_access
|
116
136
|
locale = opts.delete(:locale) || Mobility.locale
|
117
137
|
|
118
|
-
|
119
|
-
return yield if maps.empty?
|
120
|
-
|
121
|
-
base = opts.empty? ? scope : yield(opts)
|
122
|
-
maps.inject(base) { |rel, map| map[rel] }
|
138
|
+
_build(scope, opts, locale, invert, &block)
|
123
139
|
end
|
124
140
|
|
125
141
|
private
|
126
142
|
|
127
|
-
|
128
|
-
|
143
|
+
# Builds a translated relation for a given opts hash and optional
|
144
|
+
# invert boolean.
|
145
|
+
def _build(scope, opts, locale, invert)
|
146
|
+
keys, predicates = opts.keys.map(&:to_s), []
|
129
147
|
|
130
|
-
scope.mobility_modules.
|
131
|
-
|
148
|
+
query_map = scope.mobility_modules.inject(IDENTITY) do |qm, mod|
|
149
|
+
i18n_keys = mod.names & keys
|
150
|
+
next qm if i18n_keys.empty?
|
132
151
|
|
133
|
-
|
152
|
+
mod_predicates = i18n_keys.map do |key|
|
134
153
|
build_predicate(scope.backend_node(key.to_sym, locale), opts.delete(key))
|
135
154
|
end
|
155
|
+
invert_predicates!(mod_predicates) if invert
|
156
|
+
predicates += mod_predicates
|
136
157
|
|
137
|
-
->(
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
158
|
+
->(r) { mod.backend_class.apply_scope(qm[r], mod_predicates, locale, invert: invert) }
|
159
|
+
end
|
160
|
+
|
161
|
+
return yield if query_map == IDENTITY
|
162
|
+
|
163
|
+
relation = opts.empty? ? scope : yield(opts)
|
164
|
+
query_map[relation.where(predicates.inject(&:and))]
|
143
165
|
end
|
144
166
|
|
145
167
|
def build_predicate(node, values)
|
@@ -156,15 +178,19 @@ enabled for any one attribute on the model.
|
|
156
178
|
Array.wrap(values).uniq.partition(&:nil?)
|
157
179
|
end
|
158
180
|
|
181
|
+
def invert_predicates!(predicates)
|
182
|
+
predicates.map!(&method(:invert_predicate))
|
183
|
+
end
|
184
|
+
|
159
185
|
# Adapted from AR::Relation::WhereClause#invert_predicate
|
160
|
-
def invert_predicate(
|
161
|
-
case
|
186
|
+
def invert_predicate(predicate)
|
187
|
+
case predicate
|
162
188
|
when ::Arel::Nodes::In
|
163
|
-
::Arel::Nodes::NotIn.new(
|
189
|
+
::Arel::Nodes::NotIn.new(predicate.left, predicate.right)
|
164
190
|
when ::Arel::Nodes::Equality
|
165
|
-
::Arel::Nodes::NotEqual.new(
|
191
|
+
::Arel::Nodes::NotEqual.new(predicate.left, predicate.right)
|
166
192
|
else
|
167
|
-
::Arel::Nodes::Not.new(
|
193
|
+
::Arel::Nodes::Not.new(predicate)
|
168
194
|
end
|
169
195
|
end
|
170
196
|
end
|
@@ -10,7 +10,8 @@ locales directly with a method call, using a suffix including the locale:
|
|
10
10
|
article.title_pt_br
|
11
11
|
|
12
12
|
If no locales are passed as an option to the initializer,
|
13
|
-
+
|
13
|
+
+Mobility.available_locales+ (i.e. +I18n.available_locales+, or Rails-set
|
14
|
+
available locales for a Rails application) will be used by default.
|
14
15
|
|
15
16
|
@example
|
16
17
|
class Post
|
@@ -41,7 +42,7 @@ If no locales are passed as an option to the initializer,
|
|
41
42
|
|
42
43
|
# @param [String] One or more attribute names
|
43
44
|
# @param [Array<Symbol>] Locales
|
44
|
-
def initialize(*attribute_names, locales:
|
45
|
+
def initialize(*attribute_names, locales:)
|
45
46
|
attribute_names.each do |name|
|
46
47
|
locales.each do |locale|
|
47
48
|
define_reader(name, locale)
|
@@ -20,6 +20,9 @@ module Mobility
|
|
20
20
|
if Loaded::ActiveRecord && attributes.model_class < ::ActiveRecord::Base
|
21
21
|
require "mobility/plugins/active_record/query"
|
22
22
|
ActiveRecord::Query.apply(attributes)
|
23
|
+
elsif Loaded::Sequel && attributes.model_class < ::Sequel::Model
|
24
|
+
require "mobility/plugins/sequel/query"
|
25
|
+
Sequel::Query.apply(attributes)
|
23
26
|
end
|
24
27
|
end
|
25
28
|
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
module Mobility
|
3
|
+
module Plugins
|
4
|
+
=begin
|
5
|
+
|
6
|
+
See ActiveRecord::Query plugin.
|
7
|
+
|
8
|
+
=end
|
9
|
+
module Sequel
|
10
|
+
module Query
|
11
|
+
class << self
|
12
|
+
def apply(attributes)
|
13
|
+
attributes.model_class.class_eval do
|
14
|
+
extend QueryMethod
|
15
|
+
singleton_class.send :alias_method, Mobility.query_method, :__mobility_query_dataset__
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module QueryMethod
|
21
|
+
def __mobility_query_dataset__(locale: Mobility.locale, &block)
|
22
|
+
if block_given?
|
23
|
+
VirtualRow.build_query(self, locale, &block)
|
24
|
+
else
|
25
|
+
dataset.with_extend(QueryExtension)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Internal class to create a "clean room" for manipulating translated
|
31
|
+
# attribute nodes in an instance-eval'ed block. Inspired by Sequel's
|
32
|
+
# (much more sophisticated) virtual rows.
|
33
|
+
class VirtualRow < BasicObject
|
34
|
+
attr_reader :__backends
|
35
|
+
|
36
|
+
def initialize(model_class, locale)
|
37
|
+
@model_class, @locale, @__backends = model_class, locale, []
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(m, *)
|
41
|
+
if @model_class.mobility_attribute?(m)
|
42
|
+
@__backends |= [@model_class.mobility_backend_class(m)]
|
43
|
+
@model_class.mobility_backend_class(m).build_op(m.to_s, @locale)
|
44
|
+
elsif @model_class.columns.include?(m.to_s)
|
45
|
+
::Sequel::SQL::QualifiedIdentifier.new(@model_class.table_name, m)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def build_query(klass, locale, &block)
|
53
|
+
row = new(klass, locale)
|
54
|
+
query = block.arity.zero? ? row.instance_eval(&block) : block.call(row)
|
55
|
+
|
56
|
+
if ::Sequel::Dataset === query
|
57
|
+
predicates = query.opts[:where]
|
58
|
+
prepare_datasets(query, row.__backends, locale, predicates)
|
59
|
+
else
|
60
|
+
prepare_datasets(klass.dataset, row.__backends, locale, query).where(query)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def prepare_datasets(dataset, backends, locale, predicates)
|
67
|
+
backends.inject(dataset) { |ds, b| b.prepare_dataset(ds, predicates, locale) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
private_constant :QueryMethod, :VirtualRow
|
72
|
+
|
73
|
+
module QueryExtension
|
74
|
+
%w[exclude or where].each do |method_name|
|
75
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
76
|
+
def #{method_name}(*conds, &block)
|
77
|
+
QueryBuilder.build(self, #{method_name.inspect}, conds) do |untranslated_conds|
|
78
|
+
untranslated_conds ? super(untranslated_conds, &block) : super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
EOM
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return backend node for attribute name.
|
85
|
+
# @param [Symbol,String] name Name of attribute
|
86
|
+
# @param [Symbol] locale Locale
|
87
|
+
# @return [Arel::Node] Arel node for this attribute in given locale
|
88
|
+
def backend_op(name, locale = Mobility.locale)
|
89
|
+
model.mobility_backend_class(name)[name, locale]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module QueryBuilder
|
94
|
+
IDENTITY = ->(x) { x }.freeze
|
95
|
+
|
96
|
+
class << self
|
97
|
+
def build(dataset, query_method, query_conds, &block)
|
98
|
+
return yield unless Hash === query_conds.first
|
99
|
+
|
100
|
+
cond = query_conds.first.dup
|
101
|
+
locale = cond.delete(:locale) || Mobility.locale
|
102
|
+
|
103
|
+
_build(dataset, cond, locale, query_method, &block)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def _build(dataset, cond, locale, query_method)
|
109
|
+
keys, predicates = cond.keys, []
|
110
|
+
model = dataset.model
|
111
|
+
|
112
|
+
query_map = model.mobility_modules.inject(IDENTITY) do |qm, mod|
|
113
|
+
i18n_keys = mod.names.map(&:to_sym) & keys
|
114
|
+
next qm if i18n_keys.empty?
|
115
|
+
|
116
|
+
mod_predicates = i18n_keys.map do |key|
|
117
|
+
build_predicate(dataset.backend_op(key, locale), cond.delete(key))
|
118
|
+
end
|
119
|
+
predicates += mod_predicates
|
120
|
+
|
121
|
+
->(ds) { mod.backend_class.prepare_dataset(qm[ds], mod_predicates, locale) }
|
122
|
+
end
|
123
|
+
|
124
|
+
return yield if query_map == IDENTITY
|
125
|
+
|
126
|
+
predicates = ::Sequel.&(*predicates, cond) unless cond.empty?
|
127
|
+
query_map[dataset.public_send(query_method, ::Sequel.&(*predicates))]
|
128
|
+
end
|
129
|
+
|
130
|
+
def build_predicate(op, values)
|
131
|
+
vals = values.is_a?(Array) ? values.uniq: [values]
|
132
|
+
vals = vals.first if vals.size == 1
|
133
|
+
op =~ vals
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|