mobility 0.7.6 → 0.8.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 +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
|