mobility 0.3.6 → 0.4.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 +21 -0
- data/Gemfile +11 -9
- data/Gemfile.lock +9 -6
- data/README.md +50 -16
- data/lib/mobility.rb +18 -4
- data/lib/mobility/active_record.rb +14 -7
- data/lib/mobility/active_record/uniqueness_validator.rb +17 -3
- data/lib/mobility/attributes.rb +39 -16
- data/lib/mobility/backends/active_record/column/query_methods.rb +12 -11
- data/lib/mobility/backends/active_record/container.rb +116 -0
- data/lib/mobility/backends/active_record/container/query_methods.rb +31 -0
- data/lib/mobility/backends/active_record/hstore/query_methods.rb +7 -54
- data/lib/mobility/backends/active_record/jsonb/query_methods.rb +5 -55
- data/lib/mobility/backends/active_record/key_value.rb +13 -9
- data/lib/mobility/backends/active_record/key_value/query_methods.rb +6 -6
- data/lib/mobility/backends/active_record/pg_query_methods.rb +115 -0
- data/lib/mobility/backends/active_record/query_methods.rb +4 -3
- data/lib/mobility/backends/active_record/serialized/query_methods.rb +6 -5
- data/lib/mobility/backends/active_record/table.rb +18 -3
- data/lib/mobility/backends/active_record/table/query_methods.rb +25 -14
- data/lib/mobility/backends/container.rb +25 -0
- data/lib/mobility/backends/hash_valued.rb +2 -2
- data/lib/mobility/backends/null.rb +2 -2
- data/lib/mobility/backends/sequel/column.rb +2 -2
- data/lib/mobility/backends/sequel/column/query_methods.rb +2 -2
- data/lib/mobility/backends/sequel/container.rb +99 -0
- data/lib/mobility/backends/sequel/container/query_methods.rb +41 -0
- data/lib/mobility/backends/sequel/hstore/query_methods.rb +17 -3
- data/lib/mobility/backends/sequel/jsonb/query_methods.rb +17 -3
- data/lib/mobility/backends/sequel/key_value.rb +2 -1
- data/lib/mobility/backends/sequel/key_value/query_methods.rb +2 -2
- data/lib/mobility/backends/sequel/pg_hash.rb +4 -7
- data/lib/mobility/backends/sequel/pg_query_methods.rb +85 -0
- data/lib/mobility/backends/sequel/query_methods.rb +4 -3
- data/lib/mobility/backends/sequel/serialized/query_methods.rb +4 -3
- data/lib/mobility/backends/sequel/table.rb +1 -1
- data/lib/mobility/backends/sequel/table/query_methods.rb +2 -2
- data/lib/mobility/backends/serialized.rb +5 -7
- data/lib/mobility/configuration.rb +34 -4
- data/lib/mobility/plugins/active_record/dirty.rb +1 -1
- data/lib/mobility/plugins/fallbacks.rb +20 -6
- data/lib/mobility/sequel/column_changes.rb +2 -5
- data/lib/mobility/sequel/hash_initializer.rb +19 -0
- data/lib/mobility/util.rb +0 -2
- data/lib/mobility/version.rb +1 -1
- metadata +11 -4
- metadata.gz.sig +0 -0
- data/lib/mobility/backends/sequel/postgres_query_methods.rb +0 -41
@@ -5,17 +5,10 @@ module Mobility
|
|
5
5
|
class ActiveRecord::Column::QueryMethods < ActiveRecord::QueryMethods
|
6
6
|
def initialize(attributes, _)
|
7
7
|
super
|
8
|
-
|
9
|
-
@opts_converter = opts_converter = lambda do |opts|
|
10
|
-
if i18n_keys = attributes_extractor.call(opts)
|
11
|
-
opts = opts.with_indifferent_access
|
12
|
-
i18n_keys.each { |attr| opts[Column.column_name_for(attr)] = opts.delete(attr) }
|
13
|
-
end
|
14
|
-
return opts
|
15
|
-
end
|
8
|
+
q = self
|
16
9
|
|
17
10
|
define_method :where! do |opts, *rest|
|
18
|
-
super(
|
11
|
+
super(q.convert_opts(opts), *rest)
|
19
12
|
end
|
20
13
|
|
21
14
|
attributes.each do |attribute|
|
@@ -27,15 +20,23 @@ module Mobility
|
|
27
20
|
|
28
21
|
def extended(relation)
|
29
22
|
super
|
30
|
-
|
23
|
+
q = self
|
31
24
|
|
32
25
|
mod = Module.new do
|
33
26
|
define_method :not do |opts, *rest|
|
34
|
-
super(
|
27
|
+
super(q.convert_opts(opts), *rest)
|
35
28
|
end
|
36
29
|
end
|
37
30
|
relation.mobility_where_chain.include(mod)
|
38
31
|
end
|
32
|
+
|
33
|
+
def convert_opts(opts)
|
34
|
+
if i18n_keys = extract_attributes(opts)
|
35
|
+
opts = opts.with_indifferent_access
|
36
|
+
i18n_keys.each { |attr| opts[Column.column_name_for(attr)] = opts.delete(attr) }
|
37
|
+
end
|
38
|
+
opts
|
39
|
+
end
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backends
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
|
6
|
+
|
7
|
+
=end
|
8
|
+
class ActiveRecord::Container
|
9
|
+
include ActiveRecord
|
10
|
+
|
11
|
+
require 'mobility/backends/active_record/container/query_methods'
|
12
|
+
|
13
|
+
# @return [Symbol] name of container column
|
14
|
+
attr_reader :column_name
|
15
|
+
|
16
|
+
# @!macro backend_constructor
|
17
|
+
# @option options [Symbol] column_name Name of container column
|
18
|
+
def initialize(model, attribute, options = {})
|
19
|
+
super
|
20
|
+
@column_name = options[:column_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!group Backend Accessors
|
24
|
+
#
|
25
|
+
# @note Translation may be a string, integer, boolean, hash or array
|
26
|
+
# since value is stored on a JSON hash.
|
27
|
+
# @param [Symbol] locale Locale to read
|
28
|
+
# @param [Hash] options
|
29
|
+
# @return [String,Integer,Boolean] Value of translation
|
30
|
+
def read(locale, _ = nil)
|
31
|
+
model_translations(locale)[attribute]
|
32
|
+
end
|
33
|
+
|
34
|
+
# @note Translation may be a string, integer, boolean, hash or array
|
35
|
+
# since value is stored on a JSON hash.
|
36
|
+
# @param [Symbol] locale Locale to write
|
37
|
+
# @param [String,Integer,Boolean] value Value to write
|
38
|
+
# @param [Hash] options
|
39
|
+
# @return [String,Integer,Boolean] Updated value
|
40
|
+
def write(locale, value, _ = nil)
|
41
|
+
set_attribute_translation(locale, value)
|
42
|
+
model_translations(locale)[attribute]
|
43
|
+
end
|
44
|
+
# @!endgroup
|
45
|
+
|
46
|
+
# @!group Backend Configuration
|
47
|
+
# @option options [Symbol] column_name (:translations) Name of column on which to store translations
|
48
|
+
def self.configure(options)
|
49
|
+
options[:column_name] ||= :translations
|
50
|
+
end
|
51
|
+
# @!endgroup
|
52
|
+
|
53
|
+
# @!macro backend_iterator
|
54
|
+
def each_locale
|
55
|
+
model[column_name].each do |l, v|
|
56
|
+
yield l.to_sym if v.present?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
setup do |_attributes, options|
|
61
|
+
store options[:column_name], coder: Coder
|
62
|
+
|
63
|
+
# Fix for duping depth-2 jsonb column in AR < 5.0
|
64
|
+
if ::ActiveRecord::VERSION::STRING < '5.0'
|
65
|
+
column_name = options[:column_name]
|
66
|
+
module_name = "MobilityArContainer#{column_name.to_s.camelcase}"
|
67
|
+
unless const_defined?(module_name)
|
68
|
+
dupable = Module.new do
|
69
|
+
class_eval <<-EOM, __FILE__, __LINE__ + 1
|
70
|
+
def initialize_dup(source)
|
71
|
+
super
|
72
|
+
self.#{column_name} = source.#{column_name}.deep_dup
|
73
|
+
end
|
74
|
+
EOM
|
75
|
+
end
|
76
|
+
include const_set(module_name, dupable)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
setup_query_methods(QueryMethods)
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def model_translations(locale)
|
86
|
+
model[column_name][locale] ||= {}
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_attribute_translation(locale, value)
|
90
|
+
translations = model[column_name] || {}
|
91
|
+
translations[locale.to_s] ||= {}
|
92
|
+
translations[locale.to_s][attribute] = value
|
93
|
+
model[column_name] = translations
|
94
|
+
end
|
95
|
+
|
96
|
+
class Coder
|
97
|
+
def self.dump(obj)
|
98
|
+
if obj.is_a? Hash
|
99
|
+
obj.inject({}) do |translations, (locale, value)|
|
100
|
+
value.each do |k, v|
|
101
|
+
(translations[locale] ||= {})[k] = v if v.present?
|
102
|
+
end
|
103
|
+
translations
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise ArgumentError, "Attribute is supposed to be a Hash, but was a #{obj.class}. -- #{obj.inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.load(obj)
|
111
|
+
obj
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "mobility/backends/active_record/jsonb"
|
2
|
+
|
3
|
+
module Mobility
|
4
|
+
module Backends
|
5
|
+
class ActiveRecord::Container::QueryMethods < ActiveRecord::QueryMethods
|
6
|
+
include ActiveRecord::PgQueryMethods
|
7
|
+
attr_reader :column_name, :column
|
8
|
+
|
9
|
+
def initialize(_attributes, options)
|
10
|
+
super
|
11
|
+
@column_name = options[:column_name]
|
12
|
+
@column = arel_table[@column_name]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def contains_value(key, value, locale)
|
18
|
+
build_infix(:'@>',
|
19
|
+
build_infix(:'->', column, quote(locale)),
|
20
|
+
quote({ key => value }.to_json))
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_locale(key, locale)
|
24
|
+
build_infix(:'?', column, quote(locale)).and(
|
25
|
+
build_infix(:'?',
|
26
|
+
build_infix(:'->', column, quote(locale)),
|
27
|
+
quote(key)))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,62 +1,15 @@
|
|
1
|
+
require 'mobility/backends/active_record/pg_query_methods'
|
2
|
+
require 'mobility/backends/active_record/query_methods'
|
3
|
+
|
1
4
|
module Mobility
|
2
5
|
module Backends
|
3
6
|
class ActiveRecord::Hstore::QueryMethods < ActiveRecord::QueryMethods
|
4
|
-
|
5
|
-
super
|
6
|
-
attributes_extractor = @attributes_extractor
|
7
|
-
|
8
|
-
define_method :where! do |opts, *rest|
|
9
|
-
if i18n_keys = attributes_extractor.call(opts)
|
10
|
-
m = arel_table
|
11
|
-
locale = Arel::Nodes.build_quoted(Mobility.locale.to_s)
|
12
|
-
opts = opts.with_indifferent_access
|
13
|
-
infix = Arel::Nodes::InfixOperation
|
14
|
-
|
15
|
-
i18n_query = i18n_keys.map { |key|
|
16
|
-
column = m[key.to_sym]
|
17
|
-
value = opts.delete(key)
|
18
|
-
|
19
|
-
if value.nil?
|
20
|
-
infix.new(:'?', column, locale).not
|
21
|
-
else
|
22
|
-
infix.new(:'->', m[key.to_sym], locale).eq(value)
|
23
|
-
end
|
24
|
-
}.inject(&:and)
|
25
|
-
|
26
|
-
opts.empty? ? super(i18n_query) : super(opts, *rest).where(i18n_query)
|
27
|
-
else
|
28
|
-
super(opts, *rest)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def extended(relation)
|
34
|
-
super
|
35
|
-
attributes_extractor = @attributes_extractor
|
36
|
-
m = relation.model.arel_table
|
37
|
-
|
38
|
-
mod = Module.new do
|
39
|
-
define_method :not do |opts, *rest|
|
40
|
-
if i18n_keys = attributes_extractor.call(opts)
|
41
|
-
locale = Arel::Nodes.build_quoted(Mobility.locale.to_s)
|
42
|
-
opts = opts.with_indifferent_access
|
43
|
-
infix = Arel::Nodes::InfixOperation
|
7
|
+
include ActiveRecord::PgQueryMethods
|
44
8
|
|
45
|
-
|
46
|
-
column = m[key.to_sym]
|
47
|
-
value = Arel::Nodes.build_quoted(opts.delete(key).to_s)
|
48
|
-
has_key = infix.new(:'?', column, locale)
|
49
|
-
not_eq_value = infix.new(:'->', column, locale).not_eq(value)
|
50
|
-
has_key.and(not_eq_value)
|
51
|
-
}.inject(&:and)
|
9
|
+
private
|
52
10
|
|
53
|
-
|
54
|
-
|
55
|
-
super(opts, *rest)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
relation.mobility_where_chain.include(mod)
|
11
|
+
def contains_value(key, value, locale)
|
12
|
+
build_infix(:'->', arel_table[key], quote(locale)).eq(quote(value.to_s))
|
60
13
|
end
|
61
14
|
end
|
62
15
|
end
|
@@ -1,65 +1,15 @@
|
|
1
|
+
require 'mobility/backends/active_record/pg_query_methods'
|
1
2
|
require "mobility/backends/active_record/query_methods"
|
2
3
|
|
3
4
|
module Mobility
|
4
5
|
module Backends
|
5
6
|
class ActiveRecord::Jsonb::QueryMethods < ActiveRecord::QueryMethods
|
6
|
-
|
7
|
-
super
|
8
|
-
attributes_extractor = @attributes_extractor
|
7
|
+
include ActiveRecord::PgQueryMethods
|
9
8
|
|
10
|
-
|
11
|
-
if i18n_keys = attributes_extractor.call(opts)
|
12
|
-
m = arel_table
|
13
|
-
locale = Arel::Nodes.build_quoted(Mobility.locale.to_s)
|
14
|
-
opts = opts.with_indifferent_access
|
15
|
-
infix = Arel::Nodes::InfixOperation
|
9
|
+
private
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
value = opts.delete(key)
|
20
|
-
|
21
|
-
if value.nil?
|
22
|
-
infix.new(:'?', column, locale).not
|
23
|
-
else
|
24
|
-
predicate = Arel::Nodes.build_quoted({ Mobility.locale => value }.to_json)
|
25
|
-
infix.new(:'@>', m[key.to_sym], predicate)
|
26
|
-
end
|
27
|
-
}.inject(&:and)
|
28
|
-
|
29
|
-
opts.empty? ? super(i18n_query) : super(opts, *rest).where(i18n_query)
|
30
|
-
else
|
31
|
-
super(opts, *rest)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def extended(relation)
|
37
|
-
super
|
38
|
-
attributes_extractor = @attributes_extractor
|
39
|
-
m = relation.model.arel_table
|
40
|
-
|
41
|
-
mod = Module.new do
|
42
|
-
define_method :not do |opts, *rest|
|
43
|
-
if i18n_keys = attributes_extractor.call(opts)
|
44
|
-
locale = Arel::Nodes.build_quoted(Mobility.locale.to_s)
|
45
|
-
opts = opts.with_indifferent_access
|
46
|
-
infix = Arel::Nodes::InfixOperation
|
47
|
-
|
48
|
-
i18n_query = i18n_keys.map { |key|
|
49
|
-
column = m[key.to_sym]
|
50
|
-
has_key = infix.new(:'?', column, locale)
|
51
|
-
predicate = Arel::Nodes.build_quoted({ Mobility.locale => opts.delete(key) }.to_json)
|
52
|
-
not_eq_value = infix.new(:'@>', m[key.to_sym], predicate).not
|
53
|
-
has_key.and(not_eq_value)
|
54
|
-
}.inject(&:and)
|
55
|
-
|
56
|
-
super(opts, *rest).where(i18n_query)
|
57
|
-
else
|
58
|
-
super(opts, *rest)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
relation.mobility_where_chain.include(mod)
|
11
|
+
def contains_value(key, value, locale)
|
12
|
+
build_infix(:'@>', arel_table[key], quote({locale => value }.to_json))
|
63
13
|
end
|
64
14
|
end
|
65
15
|
end
|
@@ -83,15 +83,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
83
83
|
include const_set(module_name, callback_methods)
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
# Clean up *all* leftover translations of this model, only once.
|
89
|
-
def mobility_destroy_key_value_translations
|
90
|
-
[:string, :text].freeze.each do |type|
|
91
|
-
Mobility::ActiveRecord.const_get("#{type.capitalize}Translation".freeze).
|
92
|
-
where(translatable: self).destroy_all
|
93
|
-
end
|
94
|
-
end unless private_instance_methods(false).include?(:mobility_destroy_key_value_translations)
|
86
|
+
include DestroyKeyValueTranslations
|
95
87
|
end
|
96
88
|
|
97
89
|
setup_query_methods(QueryMethods)
|
@@ -104,6 +96,18 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
104
96
|
translation ||= translations.build(locale: locale, key: attribute)
|
105
97
|
translation
|
106
98
|
end
|
99
|
+
|
100
|
+
module DestroyKeyValueTranslations
|
101
|
+
private
|
102
|
+
|
103
|
+
# Clean up *all* leftover translations of this model, only once.
|
104
|
+
def mobility_destroy_key_value_translations
|
105
|
+
[:string, :text].freeze.each do |type|
|
106
|
+
Mobility::ActiveRecord.const_get("#{type.capitalize}Translation".freeze).
|
107
|
+
where(translatable: self).destroy_all
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
107
111
|
end
|
108
112
|
end
|
109
113
|
end
|
@@ -19,12 +19,12 @@ module Mobility
|
|
19
19
|
|
20
20
|
def extended(relation)
|
21
21
|
super
|
22
|
-
association_name
|
23
|
-
|
22
|
+
association_name = @association_name
|
23
|
+
q = self
|
24
24
|
|
25
25
|
mod = Module.new do
|
26
26
|
define_method :not do |opts, *rest|
|
27
|
-
if i18n_keys =
|
27
|
+
if i18n_keys = q.extract_attributes(opts)
|
28
28
|
opts = opts.with_indifferent_access
|
29
29
|
i18n_keys.each { |attr| opts["#{attr}_#{association_name}"] = { value: opts.delete(attr) }}
|
30
30
|
super(opts, *rest).send(:"join_#{association_name}", *i18n_keys)
|
@@ -54,12 +54,12 @@ module Mobility
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def define_query_methods(association_name)
|
57
|
-
|
57
|
+
q = self
|
58
58
|
|
59
59
|
define_method :where! do |opts, *rest|
|
60
|
-
if i18n_keys =
|
60
|
+
if i18n_keys = q.extract_attributes(opts)
|
61
61
|
opts = opts.with_indifferent_access
|
62
|
-
i18n_nulls = i18n_keys.
|
62
|
+
i18n_nulls = i18n_keys.reject { |key| opts[key] && Array.wrap(opts[key]).all? }
|
63
63
|
i18n_keys.each { |attr| opts["#{attr}_#{association_name}"] = { value: opts.delete(attr) }}
|
64
64
|
super(opts, *rest).
|
65
65
|
send("join_#{association_name}", *(i18n_keys - i18n_nulls)).
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backends
|
3
|
+
module ActiveRecord
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Defines query methods for Postgres backends. Including class must define a
|
7
|
+
single method, +contains_value+, which accepts a column, value and locale to
|
8
|
+
match, and returns an Arel node.
|
9
|
+
|
10
|
+
This module avoids 99% duplication between hstore and jsonb backend querying
|
11
|
+
code.
|
12
|
+
|
13
|
+
@see Mobility::Backends::ActiveRecord::Jsonb::QueryMethods
|
14
|
+
@see Mobility::Backends::ActiveRecord::Hstore::QueryMethods
|
15
|
+
|
16
|
+
=end
|
17
|
+
module PgQueryMethods
|
18
|
+
attr_reader :arel_table
|
19
|
+
|
20
|
+
def initialize(attributes, options)
|
21
|
+
super
|
22
|
+
@arel_table = options[:model_class].arel_table
|
23
|
+
|
24
|
+
q = self
|
25
|
+
|
26
|
+
define_method :where! do |opts, *rest|
|
27
|
+
if i18n_keys = q.extract_attributes(opts)
|
28
|
+
opts = opts.with_indifferent_access
|
29
|
+
query = q.create_where_query!(opts, i18n_keys)
|
30
|
+
|
31
|
+
opts.empty? ? super(query) : super(opts, *rest).where(query)
|
32
|
+
else
|
33
|
+
super(opts, *rest)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def extended(relation)
|
39
|
+
super
|
40
|
+
q = self
|
41
|
+
|
42
|
+
mod = Module.new do
|
43
|
+
define_method :not do |opts, *rest|
|
44
|
+
if i18n_keys = q.extract_attributes(opts)
|
45
|
+
opts = opts.with_indifferent_access
|
46
|
+
query = q.create_not_query!(opts, i18n_keys)
|
47
|
+
|
48
|
+
super(opts, *rest).where(query)
|
49
|
+
else
|
50
|
+
super(opts, *rest)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
relation.mobility_where_chain.include(mod)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create +where+ query for options hash, translated keys and arel_table
|
58
|
+
# @note This is a destructive operation, it will modify +opts+.
|
59
|
+
#
|
60
|
+
# @param [Hash] opts Hash of attribute/value pairs
|
61
|
+
# @param [Array] keys Translated attribute names
|
62
|
+
# @return [Arel::Node] Arel node to pass to +where+
|
63
|
+
def create_where_query!(opts, keys)
|
64
|
+
locale = Mobility.locale
|
65
|
+
keys.map { |key|
|
66
|
+
values = opts.delete(key)
|
67
|
+
|
68
|
+
next has_locale(key, locale).not if values.nil?
|
69
|
+
|
70
|
+
Array.wrap(values).map { |value|
|
71
|
+
value.nil? ?
|
72
|
+
has_locale(key, locale).not :
|
73
|
+
contains_value(key, value, locale)
|
74
|
+
}.inject(&:or)
|
75
|
+
}.inject(&:and)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create +not+ query for options hash and translated keys
|
79
|
+
# @note This is a destructive operation, it will modify +opts+.
|
80
|
+
#
|
81
|
+
# @param [Hash] opts Hash of attribute/value pairs
|
82
|
+
# @param [Array] keys Translated attribute names
|
83
|
+
# @return [Arel::Node] Arel node to pass to +where+
|
84
|
+
def create_not_query!(opts, keys)
|
85
|
+
locale = Mobility.locale
|
86
|
+
keys.map { |key|
|
87
|
+
values = opts.delete(key)
|
88
|
+
|
89
|
+
Array.wrap(values).map { |value|
|
90
|
+
contains_value(key, value, locale).not
|
91
|
+
}.inject(has_locale(key, locale), &:and)
|
92
|
+
}.inject(&:and)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def contains_value(_key, _value, _locale)
|
98
|
+
raise NotImplementedError
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_locale(key, locale)
|
102
|
+
build_infix(:'?', arel_table[key], quote(locale))
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_infix(*args)
|
106
|
+
Arel::Nodes::InfixOperation.new(*args)
|
107
|
+
end
|
108
|
+
|
109
|
+
def quote(value)
|
110
|
+
Arel::Nodes.build_quoted(value.to_s)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|