mobility 0.3.6 → 0.4.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 +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
|