mobility 0.6.0 → 0.7.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 +5 -5
- checksums.yaml.gz.sig +3 -2
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +39 -1
- data/Gemfile.lock +65 -10
- data/README.md +63 -27
- data/lib/mobility.rb +16 -31
- data/lib/mobility/active_record.rb +2 -12
- data/lib/mobility/active_record/uniqueness_validator.rb +9 -8
- data/lib/mobility/arel.rb +20 -0
- data/lib/mobility/arel/nodes.rb +16 -0
- data/lib/mobility/arel/nodes/pg_ops.rb +136 -0
- data/lib/mobility/arel/visitor.rb +61 -0
- data/lib/mobility/attributes.rb +82 -19
- data/lib/mobility/backend.rb +53 -8
- data/lib/mobility/backend_resetter.rb +2 -1
- data/lib/mobility/backends/active_record.rb +31 -11
- data/lib/mobility/backends/active_record/column.rb +7 -3
- data/lib/mobility/backends/active_record/container.rb +23 -21
- data/lib/mobility/backends/active_record/hstore.rb +11 -6
- data/lib/mobility/backends/active_record/json.rb +22 -16
- data/lib/mobility/backends/active_record/jsonb.rb +22 -16
- data/lib/mobility/backends/active_record/key_value.rb +123 -15
- data/lib/mobility/backends/active_record/pg_hash.rb +1 -2
- data/lib/mobility/backends/active_record/serialized.rb +7 -6
- data/lib/mobility/backends/active_record/table.rb +145 -24
- data/lib/mobility/backends/hash_valued.rb +15 -10
- data/lib/mobility/backends/key_value.rb +12 -12
- data/lib/mobility/backends/sequel/container.rb +3 -9
- data/lib/mobility/backends/sequel/hstore.rb +2 -2
- data/lib/mobility/backends/sequel/json.rb +15 -15
- data/lib/mobility/backends/sequel/jsonb.rb +14 -14
- data/lib/mobility/backends/sequel/key_value.rb +0 -11
- data/lib/mobility/backends/sequel/pg_hash.rb +2 -3
- data/lib/mobility/backends/sequel/pg_query_methods.rb +1 -1
- data/lib/mobility/backends/sequel/query_methods.rb +3 -3
- data/lib/mobility/backends/sequel/serialized.rb +2 -2
- data/lib/mobility/backends/sequel/table.rb +10 -11
- data/lib/mobility/backends/table.rb +17 -8
- data/lib/mobility/configuration.rb +4 -1
- data/lib/mobility/interface.rb +0 -0
- data/lib/mobility/plugins.rb +1 -0
- data/lib/mobility/plugins/active_record/query.rb +192 -0
- data/lib/mobility/plugins/cache.rb +1 -2
- data/lib/mobility/plugins/default.rb +28 -14
- data/lib/mobility/plugins/fallbacks.rb +1 -1
- data/lib/mobility/plugins/locale_accessors.rb +13 -9
- data/lib/mobility/plugins/presence.rb +15 -7
- data/lib/mobility/plugins/query.rb +28 -0
- data/lib/mobility/translates.rb +9 -9
- data/lib/mobility/version.rb +1 -1
- data/lib/rails/generators/mobility/templates/initializer.rb +1 -0
- metadata +10 -15
- metadata.gz.sig +0 -0
- data/lib/mobility/accumulator.rb +0 -33
- data/lib/mobility/adapter.rb +0 -20
- data/lib/mobility/backends/active_record/column/query_methods.rb +0 -42
- data/lib/mobility/backends/active_record/container/json_query_methods.rb +0 -36
- data/lib/mobility/backends/active_record/container/jsonb_query_methods.rb +0 -33
- data/lib/mobility/backends/active_record/hstore/query_methods.rb +0 -25
- data/lib/mobility/backends/active_record/json/query_methods.rb +0 -30
- data/lib/mobility/backends/active_record/jsonb/query_methods.rb +0 -26
- data/lib/mobility/backends/active_record/key_value/query_methods.rb +0 -76
- data/lib/mobility/backends/active_record/pg_query_methods.rb +0 -154
- data/lib/mobility/backends/active_record/serialized/query_methods.rb +0 -34
- data/lib/mobility/backends/active_record/table/query_methods.rb +0 -105
@@ -20,8 +20,9 @@ Resets backend cache when reset events occur.
|
|
20
20
|
# @raise [ArgumentError] if no block is provided.
|
21
21
|
def initialize(attribute_names, &block)
|
22
22
|
raise ArgumentError, "block required" unless block_given?
|
23
|
+
names = attribute_names.map(&:to_sym)
|
23
24
|
@model_reset_method = Proc.new do
|
24
|
-
|
25
|
+
names.each do |name|
|
25
26
|
if @mobility_backends && @mobility_backends[name]
|
26
27
|
@mobility_backends[name].instance_eval(&block)
|
27
28
|
end
|
@@ -1,19 +1,39 @@
|
|
1
1
|
module Mobility
|
2
2
|
module Backends
|
3
3
|
module ActiveRecord
|
4
|
-
def setup_query_methods(query_methods)
|
5
|
-
setup do |attributes, options|
|
6
|
-
extend(Module.new do
|
7
|
-
define_method ::Mobility.query_method do
|
8
|
-
super().extending(query_methods.new(attributes, options))
|
9
|
-
end
|
10
|
-
end)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
4
|
def self.included(backend_class)
|
15
5
|
backend_class.include(Backend)
|
16
|
-
backend_class.extend(
|
6
|
+
backend_class.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
# @param [Symbol] name Attribute name
|
11
|
+
# @param [Symbol] locale Locale
|
12
|
+
def [](name, locale)
|
13
|
+
build_node(name.to_s, locale)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [String] _attr Attribute name
|
17
|
+
# @param [Symbol] _locale Locale
|
18
|
+
# @return Arel node for this translated attribute
|
19
|
+
def build_node(_attr, _locale)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [ActiveRecord::Relation] relation Relation to scope
|
24
|
+
# @param [Object] predicate Arel predicate
|
25
|
+
# @param [Symbol] locale Locale
|
26
|
+
# @option [Boolean] invert
|
27
|
+
# @return [ActiveRecord::Relation] Relation with scope added
|
28
|
+
def apply_scope(relation, _predicate, _locale, invert: false)
|
29
|
+
relation
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def build_quoted(value)
|
35
|
+
::Arel::Nodes.build_quoted(value)
|
36
|
+
end
|
17
37
|
end
|
18
38
|
end
|
19
39
|
end
|
@@ -34,8 +34,6 @@ or locales.)
|
|
34
34
|
include ActiveRecord
|
35
35
|
include Column
|
36
36
|
|
37
|
-
require 'mobility/backends/active_record/column/query_methods'
|
38
|
-
|
39
37
|
# @!group Backend Accessors
|
40
38
|
# @!macro backend_reader
|
41
39
|
def read(locale, _ = {})
|
@@ -53,7 +51,13 @@ or locales.)
|
|
53
51
|
available_locales.each { |l| yield(l) if present?(l) }
|
54
52
|
end
|
55
53
|
|
56
|
-
|
54
|
+
# @param [String] attr Attribute name
|
55
|
+
# @param [Symbol] locale Locale
|
56
|
+
# @return [Arel::Attributes::Attribute] Arel node for translation column
|
57
|
+
# on model table
|
58
|
+
def self.build_node(attr, locale)
|
59
|
+
model_class.arel_table[Column.column_name_for(attr, locale)]
|
60
|
+
end
|
57
61
|
|
58
62
|
private
|
59
63
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "mobility/backends/active_record"
|
3
|
+
require "mobility/arel/nodes/pg_ops"
|
3
4
|
|
4
5
|
module Mobility
|
5
6
|
module Backends
|
@@ -11,18 +12,14 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
|
|
11
12
|
class ActiveRecord::Container
|
12
13
|
include ActiveRecord
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
# @!method column_name
|
16
|
+
# Returns name of json or jsonb column used to store translations
|
17
|
+
# @return [Symbol] (:translations) Name of translations column
|
18
|
+
option_reader :column_name
|
16
19
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
# @!macro backend_constructor
|
21
|
-
# @option options [Symbol] column_name Name of container column
|
22
|
-
def initialize(model, attribute, options = {})
|
23
|
-
super
|
24
|
-
@column_name = options[:column_name]
|
25
|
-
end
|
20
|
+
# @!method column_type
|
21
|
+
# @return [Symbol] Either :json or :jsonb
|
22
|
+
option_reader :column_type
|
26
23
|
|
27
24
|
# @!group Backend Accessors
|
28
25
|
#
|
@@ -60,6 +57,20 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
|
|
60
57
|
end
|
61
58
|
# @!endgroup
|
62
59
|
|
60
|
+
# @param [String] attr Attribute name
|
61
|
+
# @param [Symbol] locale Locale
|
62
|
+
# @return [Mobility::Arel::Nodes::Json,Mobility::Arel::Nodes::Jsonb] Arel
|
63
|
+
# node for attribute on json or jsonb column
|
64
|
+
def self.build_node(attr, locale)
|
65
|
+
column = model_class.arel_table[column_name]
|
66
|
+
case column_type
|
67
|
+
when :json
|
68
|
+
Arel::Nodes::JsonContainer.new(column, build_quoted(locale), build_quoted(attr))
|
69
|
+
when :jsonb
|
70
|
+
Arel::Nodes::JsonbContainer.new(column, build_quoted(locale), build_quoted(attr))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
63
74
|
# @!macro backend_iterator
|
64
75
|
def each_locale
|
65
76
|
model[column_name].each do |l, v|
|
@@ -67,9 +78,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
|
|
67
78
|
end
|
68
79
|
end
|
69
80
|
|
70
|
-
|
71
|
-
|
72
|
-
setup do |attributes, options|
|
81
|
+
setup do |_attributes, options|
|
73
82
|
store options[:column_name], coder: Coder
|
74
83
|
|
75
84
|
# Fix for duping depth-2 jsonb column in AR < 5.0
|
@@ -88,13 +97,6 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
|
|
88
97
|
include const_set(module_name, dupable)
|
89
98
|
end
|
90
99
|
end
|
91
|
-
|
92
|
-
query_methods = backend_class.const_get("#{options[:column_type].capitalize}QueryMethods")
|
93
|
-
extend(Module.new do
|
94
|
-
define_method ::Mobility.query_method do
|
95
|
-
super().extending(query_methods.new(attributes, options))
|
96
|
-
end
|
97
|
-
end)
|
98
100
|
end
|
99
101
|
|
100
102
|
private
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mobility/backends/active_record/pg_hash'
|
2
|
+
require 'mobility/arel/nodes/pg_ops'
|
2
3
|
|
3
4
|
module Mobility
|
4
5
|
module Backends
|
@@ -6,25 +7,29 @@ module Mobility
|
|
6
7
|
|
7
8
|
Implements the {Mobility::Backends::Hstore} backend for ActiveRecord models.
|
8
9
|
|
9
|
-
@see Mobility::Backends::
|
10
|
+
@see Mobility::Backends::HashValued
|
10
11
|
|
11
12
|
=end
|
12
13
|
module ActiveRecord
|
13
14
|
class Hstore < PgHash
|
14
|
-
require 'mobility/backends/active_record/hstore/query_methods'
|
15
|
-
|
16
15
|
# @!group Backend Accessors
|
17
16
|
# @!macro backend_reader
|
18
|
-
# @!method read(locale,
|
17
|
+
# @!method read(locale, options = {})
|
19
18
|
|
20
|
-
# @!group Backend Accessors
|
21
19
|
# @!macro backend_writer
|
22
20
|
def write(locale, value, options = {})
|
23
21
|
super(locale, value && value.to_s, options)
|
24
22
|
end
|
25
23
|
# @!endgroup
|
26
24
|
|
27
|
-
|
25
|
+
# @param [String] attr Attribute name
|
26
|
+
# @param [Symbol] locale Locale
|
27
|
+
# @return [Mobility::Arel::Nodes::Hstore] Arel node for value of
|
28
|
+
# attribute key on hstore column
|
29
|
+
def self.build_node(attr, locale)
|
30
|
+
column_name = column_affix % attr
|
31
|
+
Arel::Nodes::Hstore.new(model_class.arel_table[column_name], build_quoted(locale))
|
32
|
+
end
|
28
33
|
end
|
29
34
|
end
|
30
35
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mobility/backends/active_record/pg_hash'
|
2
|
+
require 'mobility/arel/nodes/pg_ops'
|
2
3
|
|
3
4
|
module Mobility
|
4
5
|
module Backends
|
@@ -6,32 +7,37 @@ module Mobility
|
|
6
7
|
|
7
8
|
Implements the {Mobility::Backends::Json} backend for ActiveRecord models.
|
8
9
|
|
9
|
-
@see Mobility::Backends::
|
10
|
+
@see Mobility::Backends::HashValued
|
10
11
|
|
11
12
|
=end
|
12
13
|
module ActiveRecord
|
13
14
|
class Json < PgHash
|
14
|
-
require 'mobility/backends/active_record/json/query_methods'
|
15
|
-
|
16
15
|
# @!group Backend Accessors
|
17
16
|
#
|
18
|
-
# @note Translation may be string, integer or boolean-valued since
|
19
|
-
# value is stored on a JSON hash.
|
20
|
-
# @param [Symbol] locale Locale to read
|
21
|
-
# @param [Hash] options
|
22
|
-
# @return [String,Integer,Boolean] Value of translation
|
23
17
|
# @!method read(locale, **options)
|
18
|
+
# @note Translation may be string, integer or boolean-valued since
|
19
|
+
# value is stored on a JSON hash.
|
20
|
+
# @param [Symbol] locale Locale to read
|
21
|
+
# @param [Hash] options
|
22
|
+
# @return [String,Integer,Boolean] Value of translation
|
24
23
|
|
25
|
-
# @!group Backend Accessors
|
26
|
-
# @note Translation may be string, integer or boolean-valued since
|
27
|
-
# value is stored on a JSON hash.
|
28
|
-
# @param [Symbol] locale Locale to write
|
29
|
-
# @param [String,Integer,Boolean] value Value to write
|
30
|
-
# @param [Hash] options
|
31
|
-
# @return [String,Integer,Boolean] Updated value
|
32
24
|
# @!method write(locale, value, **options)
|
25
|
+
# @note Translation may be string, integer or boolean-valued since
|
26
|
+
# value is stored on a JSON hash.
|
27
|
+
# @param [Symbol] locale Locale to write
|
28
|
+
# @param [String,Integer,Boolean] value Value to write
|
29
|
+
# @param [Hash] options
|
30
|
+
# @return [String,Integer,Boolean] Updated value
|
31
|
+
# @!endgroup
|
33
32
|
|
34
|
-
|
33
|
+
# @param [String] attr Attribute name
|
34
|
+
# @param [Symbol] locale Locale
|
35
|
+
# @return [Mobility::Arel::Nodes::Json] Arel node for value of
|
36
|
+
# attribute key on jsonb column
|
37
|
+
def self.build_node(attr, locale)
|
38
|
+
column_name = column_affix % attr
|
39
|
+
Arel::Nodes::Json.new(model_class.arel_table[column_name], build_quoted(locale))
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mobility/backends/active_record/pg_hash'
|
2
|
+
require 'mobility/arel/nodes/pg_ops'
|
2
3
|
|
3
4
|
module Mobility
|
4
5
|
module Backends
|
@@ -6,32 +7,37 @@ module Mobility
|
|
6
7
|
|
7
8
|
Implements the {Mobility::Backends::Jsonb} backend for ActiveRecord models.
|
8
9
|
|
9
|
-
@see Mobility::Backends::
|
10
|
+
@see Mobility::Backends::HashValued
|
10
11
|
|
11
12
|
=end
|
12
13
|
module ActiveRecord
|
13
14
|
class Jsonb < PgHash
|
14
|
-
require 'mobility/backends/active_record/jsonb/query_methods'
|
15
|
-
|
16
15
|
# @!group Backend Accessors
|
17
16
|
#
|
18
|
-
# @note Translation may be any json type, but querying will only work on
|
19
|
-
# string-typed values.
|
20
|
-
# @param [Symbol] locale Locale to read
|
21
|
-
# @param [Hash] options
|
22
|
-
# @return [String,Integer,Boolean] Value of translation
|
23
17
|
# @!method read(locale, **options)
|
18
|
+
# @note Translation may be any json type, but querying will only work on
|
19
|
+
# string-typed values.
|
20
|
+
# @param [Symbol] locale Locale to read
|
21
|
+
# @param [Hash] options
|
22
|
+
# @return [String,Integer,Boolean] Value of translation
|
24
23
|
|
25
|
-
# @!group Backend Accessors
|
26
|
-
# @note Translation may be any json type, but querying will only work on
|
27
|
-
# string-typed values.
|
28
|
-
# @param [Symbol] locale Locale to write
|
29
|
-
# @param [String,Integer,Boolean] value Value to write
|
30
|
-
# @param [Hash] options
|
31
|
-
# @return [String,Integer,Boolean] Updated value
|
32
24
|
# @!method write(locale, value, **options)
|
25
|
+
# @note Translation may be any json type, but querying will only work on
|
26
|
+
# string-typed values.
|
27
|
+
# @param [Symbol] locale Locale to write
|
28
|
+
# @param [String,Integer,Boolean] value Value to write
|
29
|
+
# @param [Hash] options
|
30
|
+
# @return [String,Integer,Boolean] Updated value
|
31
|
+
# @!endgroup
|
33
32
|
|
34
|
-
|
33
|
+
# @param [String] attr Attribute name
|
34
|
+
# @param [Symbol] locale Locale
|
35
|
+
# @return [Mobility::Arel::Nodes::Jsonb] Arel node for value of
|
36
|
+
# attribute key on jsonb column
|
37
|
+
def self.build_node(attr, locale)
|
38
|
+
column_name = column_affix % attr
|
39
|
+
Arel::Nodes::Jsonb.new(model_class.arel_table[column_name], build_quoted(locale))
|
40
|
+
end
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
@@ -11,7 +11,7 @@ module Mobility
|
|
11
11
|
Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
12
12
|
|
13
13
|
@example
|
14
|
-
class Post <
|
14
|
+
class Post < ApplicationRecord
|
15
15
|
extend Mobility
|
16
16
|
translates :title, backend: :key_value, association_name: :translations, type: :string
|
17
17
|
end
|
@@ -29,21 +29,131 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
29
29
|
include ActiveRecord
|
30
30
|
include KeyValue
|
31
31
|
|
32
|
-
|
32
|
+
option_reader :table_alias_affix
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
class << self
|
35
|
+
# @!group Backend Configuration
|
36
|
+
# @option (see Mobility::Backends::KeyValue::ClassMethods#configure)
|
37
|
+
# @raise (see Mobility::Backends::KeyValue::ClassMethods#configure)
|
38
|
+
def configure(options)
|
39
|
+
super
|
40
|
+
if type = options[:type]
|
41
|
+
options[:association_name] ||= :"#{options[:type]}_translations"
|
42
|
+
options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
|
43
|
+
end
|
44
|
+
options[:table_alias_affix] = "#{options[:model_class]}_%s_#{options[:association_name]}"
|
45
|
+
rescue NameError
|
46
|
+
raise ArgumentError, "You must define a Mobility::ActiveRecord::#{type.capitalize}Translation class."
|
47
|
+
end
|
48
|
+
# @!endgroup
|
49
|
+
|
50
|
+
# @param [String] attr Attribute name
|
51
|
+
# @param [Symbol] _locale Locale
|
52
|
+
# @return [Mobility::Arel::Attribute] Arel attribute for aliased
|
53
|
+
# translation table value column
|
54
|
+
def build_node(attr, locale)
|
55
|
+
aliased_table = class_name.arel_table.alias(table_alias(attr, locale))
|
56
|
+
Arel::Attribute.new(aliased_table, :value, locale, self, attribute_name: attr.to_sym)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Joins translations using either INNER/OUTER join appropriate to the query.
|
60
|
+
# @param [ActiveRecord::Relation] relation Relation to scope
|
61
|
+
# @param [Object] predicate Arel predicate
|
62
|
+
# @param [Symbol] locale Locale
|
63
|
+
# @option [Boolean] invert
|
64
|
+
# @return [ActiveRecord::Relation] relation Relation with joins applied (if needed)
|
65
|
+
def apply_scope(relation, predicate, locale, invert: false)
|
66
|
+
visitor = Visitor.new(self, locale)
|
67
|
+
visitor.accept(predicate).inject(relation) do |rel, (attr, join_type)|
|
68
|
+
join_type &&= ::Arel::Nodes::InnerJoin if invert
|
69
|
+
join_translations(rel, attr, locale, join_type)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def table_alias(attr, locale)
|
76
|
+
table_alias_affix % "#{attr}_#{locale}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def join_translations(relation, key, locale, join_type)
|
80
|
+
return relation if already_joined?(relation, key, locale, join_type)
|
81
|
+
m = model_class.arel_table
|
82
|
+
t = class_name.arel_table.alias(table_alias(key, locale))
|
83
|
+
relation.joins(m.join(t, join_type).
|
84
|
+
on(t[:key].eq(key).
|
85
|
+
and(t[:locale].eq(locale).
|
86
|
+
and(t[:translatable_type].eq(model_class.base_class.name).
|
87
|
+
and(t[:translatable_id].eq(m[:id]))))).join_sources)
|
88
|
+
end
|
89
|
+
|
90
|
+
def already_joined?(relation, name, locale, join_type)
|
91
|
+
if join = get_join(relation, name, locale)
|
92
|
+
return true if (join_type == ::Arel::Nodes::OuterJoin) || (::Arel::Nodes::InnerJoin === join)
|
93
|
+
relation.joins_values = relation.joins_values - [join]
|
94
|
+
end
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_join(relation, name, locale)
|
99
|
+
relation.joins_values.find do |v|
|
100
|
+
(::Arel::Nodes::Join === v) && (v.left.name == (table_alias(name, locale)))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Internal class used to visit all nodes in a predicate clause and
|
106
|
+
# return a hash of key/value pairs corresponding to attributes (keys)
|
107
|
+
# and the respective join type (values) required for each attribute.
|
108
|
+
#
|
109
|
+
# Example:
|
110
|
+
#
|
111
|
+
# class Post < ApplicationRecord
|
112
|
+
# extend Mobility
|
113
|
+
# translates :title, :content, backend: :key_value
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# backend_class = Post.mobility_backend_class(:title)
|
117
|
+
# visitor = Mobility::Backends::ActiveRecord::KeyValue::Visitor.new(backend_class)
|
118
|
+
#
|
119
|
+
# visitor.accept(title.eq("foo").and(content.eq(nil)))
|
120
|
+
# #=> { title: Arel::Nodes::InnerJoin, content: Arel::Nodes::OuterJoin }
|
121
|
+
#
|
122
|
+
# The title predicate has a non-nil value, so we can use an INNER JOIN,
|
123
|
+
# whereas we are searching for nil content, which requires an OUTER JOIN.
|
124
|
+
#
|
125
|
+
class Visitor < Arel::Visitor
|
126
|
+
private
|
127
|
+
|
128
|
+
def visit_Arel_Nodes_Equality(object)
|
129
|
+
nils, nodes = [object.left, object.right].partition(&:nil?)
|
130
|
+
if hash = visit_collection(nodes)
|
131
|
+
hash.transform_values { nils.empty? ? INNER_JOIN : OUTER_JOIN }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def visit_collection(objects)
|
136
|
+
objects.map(&method(:visit)).compact.inject do |hash, visited|
|
137
|
+
visited.merge(hash) { |_, old, new| old == INNER_JOIN ? old : new }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
alias :visit_Array :visit_collection
|
141
|
+
|
142
|
+
def visit_Arel_Nodes_Or(object)
|
143
|
+
[object.left, object.right].map(&method(:visit)).compact.inject(&:merge).
|
144
|
+
transform_values { OUTER_JOIN }
|
145
|
+
end
|
146
|
+
|
147
|
+
def visit_Mobility_Arel_Attribute(object)
|
148
|
+
if object.backend_class == backend_class && object.locale == locale
|
149
|
+
{ object.attribute_name => INNER_JOIN }
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def visit_default(_)
|
154
|
+
{}
|
42
155
|
end
|
43
|
-
rescue NameError
|
44
|
-
raise ArgumentError, "You must define a Mobility::ActiveRecord::#{type.capitalize}Translation class."
|
45
156
|
end
|
46
|
-
# @!endgroup
|
47
157
|
|
48
158
|
setup do |attributes, options|
|
49
159
|
association_name = options[:association_name]
|
@@ -85,8 +195,6 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
85
195
|
include DestroyKeyValueTranslations
|
86
196
|
end
|
87
197
|
|
88
|
-
setup_query_methods(QueryMethods)
|
89
|
-
|
90
198
|
# Returns translation for a given locale, or builds one if none is present.
|
91
199
|
# @param [Symbol] locale
|
92
200
|
# @return [Mobility::ActiveRecord::TextTranslation,Mobility::ActiveRecord::StringTranslation]
|