mobility 0.8.13 → 1.0.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 +2 -2
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +63 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +39 -20
- data/README.md +183 -93
- data/lib/mobility.rb +101 -169
- data/lib/mobility/backend.rb +27 -51
- data/lib/mobility/backends.rb +20 -0
- data/lib/mobility/backends/active_record.rb +4 -0
- data/lib/mobility/backends/active_record/column.rb +3 -1
- data/lib/mobility/backends/active_record/container.rb +10 -11
- data/lib/mobility/backends/active_record/hstore.rb +6 -4
- data/lib/mobility/backends/active_record/json.rb +5 -3
- data/lib/mobility/backends/active_record/jsonb.rb +5 -3
- data/lib/mobility/backends/active_record/key_value.rb +31 -13
- data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
- data/lib/mobility/backends/active_record/serialized.rb +6 -0
- data/lib/mobility/backends/active_record/table.rb +17 -10
- data/lib/mobility/backends/column.rb +0 -6
- data/lib/mobility/backends/container.rb +10 -1
- data/lib/mobility/backends/hash.rb +39 -0
- data/lib/mobility/backends/hash_valued.rb +4 -0
- data/lib/mobility/backends/hstore.rb +0 -1
- data/lib/mobility/backends/json.rb +0 -1
- data/lib/mobility/backends/jsonb.rb +1 -2
- data/lib/mobility/backends/key_value.rb +31 -26
- data/lib/mobility/backends/null.rb +2 -0
- data/lib/mobility/backends/sequel.rb +37 -2
- data/lib/mobility/backends/sequel/column.rb +2 -0
- data/lib/mobility/backends/sequel/container.rb +11 -9
- data/lib/mobility/backends/sequel/hstore.rb +3 -1
- data/lib/mobility/backends/sequel/json.rb +3 -0
- data/lib/mobility/backends/sequel/jsonb.rb +3 -1
- data/lib/mobility/backends/sequel/key_value.rb +87 -18
- data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
- data/lib/mobility/backends/sequel/serialized.rb +6 -0
- data/lib/mobility/backends/sequel/table.rb +22 -9
- data/lib/mobility/backends/serialized.rb +1 -3
- data/lib/mobility/backends/table.rb +39 -31
- data/lib/mobility/pluggable.rb +56 -0
- data/lib/mobility/plugin.rb +260 -0
- data/lib/mobility/plugins.rb +27 -24
- data/lib/mobility/plugins/active_model.rb +17 -0
- data/lib/mobility/plugins/active_model/cache.rb +26 -0
- data/lib/mobility/plugins/active_model/dirty.rb +119 -78
- data/lib/mobility/plugins/active_record.rb +37 -0
- data/lib/mobility/plugins/active_record/backend.rb +27 -0
- data/lib/mobility/plugins/active_record/cache.rb +28 -0
- data/lib/mobility/plugins/active_record/dirty.rb +34 -17
- data/lib/mobility/plugins/active_record/query.rb +43 -31
- data/lib/mobility/plugins/active_record/uniqueness_validation.rb +64 -0
- data/lib/mobility/plugins/arel.rb +125 -0
- data/lib/mobility/plugins/arel/nodes.rb +15 -0
- data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
- data/lib/mobility/plugins/attribute_methods.rb +29 -20
- data/lib/mobility/plugins/attributes.rb +72 -0
- data/lib/mobility/plugins/backend.rb +161 -0
- data/lib/mobility/plugins/backend_reader.rb +34 -0
- data/lib/mobility/plugins/cache.rb +68 -26
- data/lib/mobility/plugins/default.rb +22 -17
- data/lib/mobility/plugins/dirty.rb +12 -33
- data/lib/mobility/plugins/fallbacks.rb +52 -44
- data/lib/mobility/plugins/fallthrough_accessors.rb +19 -23
- data/lib/mobility/plugins/locale_accessors.rb +22 -35
- data/lib/mobility/plugins/presence.rb +28 -21
- data/lib/mobility/plugins/query.rb +8 -17
- data/lib/mobility/plugins/reader.rb +50 -0
- data/lib/mobility/plugins/sequel.rb +34 -0
- data/lib/mobility/plugins/sequel/backend.rb +25 -0
- data/lib/mobility/plugins/sequel/cache.rb +24 -0
- data/lib/mobility/plugins/sequel/dirty.rb +34 -23
- data/lib/mobility/plugins/sequel/query.rb +21 -6
- data/lib/mobility/plugins/writer.rb +44 -0
- data/lib/mobility/translations.rb +95 -0
- data/lib/mobility/version.rb +12 -1
- data/lib/rails/generators/mobility/templates/create_string_translations.rb +0 -1
- data/lib/rails/generators/mobility/templates/create_text_translations.rb +0 -1
- data/lib/rails/generators/mobility/templates/initializer.rb +104 -78
- metadata +35 -40
- metadata.gz.sig +0 -0
- data/lib/mobility/active_model.rb +0 -4
- data/lib/mobility/active_model/backend_resetter.rb +0 -26
- data/lib/mobility/active_record.rb +0 -23
- data/lib/mobility/active_record/backend_resetter.rb +0 -26
- data/lib/mobility/active_record/model_translation.rb +0 -14
- data/lib/mobility/active_record/string_translation.rb +0 -10
- data/lib/mobility/active_record/text_translation.rb +0 -10
- data/lib/mobility/active_record/translation.rb +0 -14
- data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
- data/lib/mobility/arel.rb +0 -49
- data/lib/mobility/arel/nodes.rb +0 -13
- data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
- data/lib/mobility/arel/visitor.rb +0 -61
- data/lib/mobility/attributes.rb +0 -324
- data/lib/mobility/backend/orm_delegator.rb +0 -44
- data/lib/mobility/backend_resetter.rb +0 -50
- data/lib/mobility/configuration.rb +0 -138
- data/lib/mobility/fallbacks.rb +0 -28
- data/lib/mobility/interface.rb +0 -0
- data/lib/mobility/loaded.rb +0 -4
- data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
- data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
- data/lib/mobility/sequel.rb +0 -9
- data/lib/mobility/sequel/backend_resetter.rb +0 -23
- data/lib/mobility/sequel/column_changes.rb +0 -28
- data/lib/mobility/sequel/hash_initializer.rb +0 -21
- data/lib/mobility/sequel/model_translation.rb +0 -20
- data/lib/mobility/sequel/sql.rb +0 -16
- data/lib/mobility/sequel/string_translation.rb +0 -10
- data/lib/mobility/sequel/text_translation.rb +0 -10
- data/lib/mobility/sequel/translation.rb +0 -53
- data/lib/mobility/translates.rb +0 -73
|
@@ -40,29 +40,44 @@ locale suffix, so +title_en+, +title_pt_br+, etc.)
|
|
|
40
40
|
|
|
41
41
|
=end
|
|
42
42
|
module Dirty
|
|
43
|
-
|
|
44
|
-
# @param [Attributes] attributes
|
|
45
|
-
def included(model_class)
|
|
46
|
-
super
|
|
43
|
+
extend Plugin
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
requires :dirty, include: false
|
|
46
|
+
requires :active_model_dirty, include: :before
|
|
47
|
+
|
|
48
|
+
initialize_hook do
|
|
49
|
+
if options[:dirty]
|
|
50
|
+
include InstanceMethods
|
|
49
51
|
end
|
|
52
|
+
end
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# In earlier versions of Rails, these are needed to avoid an
|
|
55
|
-
# exception when including the AR Dirty module outside of an
|
|
56
|
-
# AR::Base class. Eventually we should be able to drop them.
|
|
57
|
-
def self.after_create; end
|
|
58
|
-
def self.after_update; end
|
|
59
|
-
|
|
60
|
-
include ::ActiveRecord::AttributeMethods::Dirty
|
|
61
|
-
end)
|
|
62
|
-
end
|
|
54
|
+
included_hook do |_, backend_class|
|
|
55
|
+
if options[:dirty]
|
|
56
|
+
backend_class.include BackendMethods
|
|
63
57
|
end
|
|
64
58
|
end
|
|
65
59
|
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def dirty_handler_methods
|
|
63
|
+
HandlerMethods
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Module which defines generic ActiveRecord::Dirty handler methods like
|
|
67
|
+
# +attribute_before_last_save+ that are patched to work with translated
|
|
68
|
+
# attributes.
|
|
69
|
+
HandlerMethods = ActiveModel::Dirty::HandlerMethodsBuilder.new(
|
|
70
|
+
Class.new do
|
|
71
|
+
# In earlier versions of Rails, these are needed to avoid an
|
|
72
|
+
# exception when including the AR Dirty module outside of an
|
|
73
|
+
# AR::Base class. Eventually we should be able to drop them.
|
|
74
|
+
def self.after_create; end
|
|
75
|
+
def self.after_update; end
|
|
76
|
+
|
|
77
|
+
include ::ActiveRecord::AttributeMethods::Dirty
|
|
78
|
+
end
|
|
79
|
+
)
|
|
80
|
+
|
|
66
81
|
module InstanceMethods
|
|
67
82
|
if ::ActiveRecord::VERSION::STRING >= '5.1' # define patterns added in 5.1
|
|
68
83
|
def saved_changes
|
|
@@ -98,5 +113,7 @@ locale suffix, so +title_en+, +title_pt_br+, etc.)
|
|
|
98
113
|
BackendMethods = ActiveModel::Dirty::BackendMethods
|
|
99
114
|
end
|
|
100
115
|
end
|
|
116
|
+
|
|
117
|
+
register_plugin(:active_record_dirty, ActiveRecord::Dirty)
|
|
101
118
|
end
|
|
102
119
|
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
|
+
require "active_record/relation"
|
|
3
|
+
|
|
2
4
|
module Mobility
|
|
3
5
|
module Plugins
|
|
4
6
|
=begin
|
|
@@ -15,45 +17,47 @@ enabled for any one attribute on the model.
|
|
|
15
17
|
=end
|
|
16
18
|
module ActiveRecord
|
|
17
19
|
module Query
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
extend Plugin
|
|
21
|
+
|
|
22
|
+
requires :query, include: false
|
|
23
|
+
|
|
24
|
+
included_hook do |klass, backend_class|
|
|
25
|
+
plugin = self
|
|
26
|
+
if options[:query]
|
|
27
|
+
raise MissingBackend, "backend required for Query plugin" unless backend_class
|
|
28
|
+
|
|
29
|
+
klass.class_eval do
|
|
21
30
|
extend QueryMethod
|
|
22
|
-
extend FindByMethods.new(*
|
|
23
|
-
singleton_class.send :alias_method,
|
|
31
|
+
extend FindByMethods.new(*plugin.names)
|
|
32
|
+
singleton_class.send :alias_method, plugin.query_method, :__mobility_query_scope__
|
|
24
33
|
end
|
|
25
|
-
|
|
34
|
+
backend_class.include BackendMethods
|
|
26
35
|
end
|
|
36
|
+
end
|
|
27
37
|
|
|
38
|
+
class << self
|
|
28
39
|
def attribute_alias(attribute, locale = Mobility.locale)
|
|
29
40
|
"__mobility_%s_%s__" % [attribute, ::Mobility.normalize_locale(locale)]
|
|
30
41
|
end
|
|
31
42
|
end
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
module BackendMethods
|
|
45
|
+
# @note We use +instance_variable_get+ here to get the +AttributeSet+
|
|
46
|
+
# rather than the hash of attributes. Getting the full hash of
|
|
47
|
+
# attributes is a performance hit and better to avoid if unnecessary.
|
|
48
|
+
# TODO: Improve this.
|
|
49
|
+
def read(locale, **)
|
|
50
|
+
if model.instance_variable_defined?(:@attributes) &&
|
|
51
|
+
(model_attributes = model.instance_variable_get(:@attributes)).key?(alias_ = Query.attribute_alias(attribute, locale))
|
|
52
|
+
model_attributes[alias_].value
|
|
53
|
+
else
|
|
54
|
+
super
|
|
55
|
+
end
|
|
43
56
|
end
|
|
44
57
|
end
|
|
45
58
|
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def model_attributes_defined?
|
|
49
|
-
model.instance_variable_defined?(:@attributes)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def model_attributes
|
|
53
|
-
model.instance_variable_get(:@attributes)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
59
|
module QueryMethod
|
|
60
|
+
# This is required for UniquenessValidator.
|
|
57
61
|
def __mobility_query_scope__(locale: Mobility.locale, &block)
|
|
58
62
|
if block_given?
|
|
59
63
|
VirtualRow.build_query(self, locale, &block)
|
|
@@ -121,7 +125,7 @@ enabled for any one attribute on the model.
|
|
|
121
125
|
case opts
|
|
122
126
|
when Symbol, String
|
|
123
127
|
@klass.mobility_attribute?(opts) ? order({ opts => :asc }, *rest) : super
|
|
124
|
-
when Hash
|
|
128
|
+
when ::Hash
|
|
125
129
|
i18n_keys, keys = opts.keys.partition(&@klass.method(:mobility_attribute?))
|
|
126
130
|
return super if i18n_keys.empty?
|
|
127
131
|
|
|
@@ -182,7 +186,7 @@ enabled for any one attribute on the model.
|
|
|
182
186
|
|
|
183
187
|
class << self
|
|
184
188
|
def build(scope, where_opts, invert: false, &block)
|
|
185
|
-
return yield unless Hash === where_opts
|
|
189
|
+
return yield unless ::Hash === where_opts
|
|
186
190
|
|
|
187
191
|
opts = where_opts.with_indifferent_access
|
|
188
192
|
locale = opts.delete(:locale) || Mobility.locale
|
|
@@ -195,11 +199,11 @@ enabled for any one attribute on the model.
|
|
|
195
199
|
# Builds a translated relation for a given opts hash and optional
|
|
196
200
|
# invert boolean.
|
|
197
201
|
def _build(scope, opts, locale, invert)
|
|
198
|
-
return yield
|
|
202
|
+
return yield if (mods = translation_modules(scope)).empty?
|
|
199
203
|
|
|
200
204
|
keys, predicates = opts.keys.map(&:to_s), []
|
|
201
205
|
|
|
202
|
-
query_map =
|
|
206
|
+
query_map = mods.inject(IDENTITY) do |qm, mod|
|
|
203
207
|
i18n_keys = mod.names & keys
|
|
204
208
|
next qm if i18n_keys.empty?
|
|
205
209
|
|
|
@@ -215,7 +219,11 @@ enabled for any one attribute on the model.
|
|
|
215
219
|
return yield if query_map == IDENTITY
|
|
216
220
|
|
|
217
221
|
relation = opts.empty? ? scope : yield(opts)
|
|
218
|
-
query_map[relation.where(predicates.inject(
|
|
222
|
+
query_map[relation.where(predicates.inject(:and))]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def translation_modules(scope)
|
|
226
|
+
scope.model.ancestors.grep(::Mobility::Translations)
|
|
219
227
|
end
|
|
220
228
|
|
|
221
229
|
def build_predicate(node, values)
|
|
@@ -267,6 +275,10 @@ enabled for any one attribute on the model.
|
|
|
267
275
|
|
|
268
276
|
private_constant :QueryExtension, :FindByMethods
|
|
269
277
|
end
|
|
278
|
+
|
|
279
|
+
class MissingBackend < Mobility::Error; end
|
|
270
280
|
end
|
|
281
|
+
|
|
282
|
+
register_plugin(:active_record_query, ActiveRecord::Query)
|
|
271
283
|
end
|
|
272
284
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Mobility
|
|
2
|
+
module Plugins
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module UniquenessValidation
|
|
5
|
+
extend Plugin
|
|
6
|
+
|
|
7
|
+
requires :query, include: false
|
|
8
|
+
|
|
9
|
+
included_hook do |klass|
|
|
10
|
+
klass.class_eval do
|
|
11
|
+
unless const_defined?(:UniquenessValidator, false)
|
|
12
|
+
self.const_set(:UniquenessValidator, Class.new(UniquenessValidator))
|
|
13
|
+
|
|
14
|
+
def self.validates_uniqueness_of(*attr_names)
|
|
15
|
+
validates_with(UniquenessValidator, _merge_attributes(attr_names))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class UniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
|
|
22
|
+
# @param [ActiveRecord::Base] record Translated model
|
|
23
|
+
# @param [String] attribute Name of attribute
|
|
24
|
+
# @param [Object] value Attribute value
|
|
25
|
+
def validate_each(record, attribute, value)
|
|
26
|
+
klass = record.class
|
|
27
|
+
|
|
28
|
+
if ([*options[:scope]] + [attribute]).any? { |name| klass.mobility_attribute?(name) }
|
|
29
|
+
return unless value.present?
|
|
30
|
+
relation = klass.unscoped.__mobility_query_scope__ do |m|
|
|
31
|
+
node = m.__send__(attribute)
|
|
32
|
+
options[:case_sensitive] == false ? node.lower.eq(value.downcase) : node.eq(value)
|
|
33
|
+
end
|
|
34
|
+
relation = relation.where.not(klass.primary_key => record.id) if record.persisted?
|
|
35
|
+
relation = mobility_scope_relation(record, relation)
|
|
36
|
+
relation = relation.merge(options[:conditions]) if options[:conditions]
|
|
37
|
+
|
|
38
|
+
if relation.exists?
|
|
39
|
+
error_options = options.except(:case_sensitive, :scope, :conditions)
|
|
40
|
+
error_options[:value] = value
|
|
41
|
+
|
|
42
|
+
record.errors.add(attribute, :taken, **error_options)
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def mobility_scope_relation(record, relation)
|
|
52
|
+
[*options[:scope]].inject(relation) do |scoped_relation, scope_item|
|
|
53
|
+
scoped_relation.__mobility_query_scope__ do |m|
|
|
54
|
+
m.__send__(scope_item).eq(record.send(scope_item))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
register_plugin(:active_record_uniqueness_validation, ActiveRecord::UniquenessValidation)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Mobility
|
|
4
|
+
module Plugins
|
|
5
|
+
=begin
|
|
6
|
+
|
|
7
|
+
Plugin for Mobility Arel customizations. Basically used as a namespace to store
|
|
8
|
+
Arel-specific classes and modules.
|
|
9
|
+
|
|
10
|
+
=end
|
|
11
|
+
module Arel
|
|
12
|
+
extend Plugin
|
|
13
|
+
|
|
14
|
+
module MobilityExpressions
|
|
15
|
+
include ::Arel::Expressions
|
|
16
|
+
|
|
17
|
+
# @note This is necessary in order to ensure that when a translated
|
|
18
|
+
# attribute is selected with an alias using +AS+, the resulting
|
|
19
|
+
# expression can still be counted without blowing up.
|
|
20
|
+
#
|
|
21
|
+
# Extending +::Arel::Expressions+ is necessary to convince ActiveRecord
|
|
22
|
+
# that this node should not be stringified, which otherwise would
|
|
23
|
+
# result in garbage SQL.
|
|
24
|
+
#
|
|
25
|
+
# @see https://github.com/rails/rails/blob/847342c25c61acaea988430dc3ab66a82e3ed486/activerecord/lib/active_record/relation/calculations.rb#L261
|
|
26
|
+
def as(*)
|
|
27
|
+
super
|
|
28
|
+
.extend(::Arel::Expressions)
|
|
29
|
+
.extend(Countable)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module Countable
|
|
33
|
+
# @note This allows expressions with selected translated attributes to
|
|
34
|
+
# be counted.
|
|
35
|
+
def count(*args)
|
|
36
|
+
left.count(*args)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class Attribute < ::Arel::Attributes::Attribute
|
|
42
|
+
include MobilityExpressions
|
|
43
|
+
|
|
44
|
+
attr_reader :backend_class
|
|
45
|
+
attr_reader :locale
|
|
46
|
+
attr_reader :attribute_name
|
|
47
|
+
|
|
48
|
+
def initialize(relation, column_name, locale, backend_class, attribute_name = nil)
|
|
49
|
+
@backend_class = backend_class
|
|
50
|
+
@locale = locale
|
|
51
|
+
@attribute_name = attribute_name || column_name
|
|
52
|
+
super(relation, column_name)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Visitor < ::Arel::Visitors::Visitor
|
|
57
|
+
INNER_JOIN = ::Arel::Nodes::InnerJoin
|
|
58
|
+
OUTER_JOIN = ::Arel::Nodes::OuterJoin
|
|
59
|
+
|
|
60
|
+
attr_reader :backend_class, :locale
|
|
61
|
+
|
|
62
|
+
def initialize(backend_class, locale)
|
|
63
|
+
super()
|
|
64
|
+
@backend_class, @locale = backend_class, locale
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def visit(*args)
|
|
70
|
+
super
|
|
71
|
+
rescue TypeError
|
|
72
|
+
visit_default(*args)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def visit_collection(_objects)
|
|
76
|
+
raise NotImplementedError
|
|
77
|
+
end
|
|
78
|
+
alias :visit_Array :visit_collection
|
|
79
|
+
|
|
80
|
+
def visit_Arel_Nodes_Unary(object)
|
|
81
|
+
visit(object.expr)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def visit_Arel_Nodes_Binary(object)
|
|
85
|
+
visit_collection([object.left, object.right])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def visit_Arel_Nodes_Function(object)
|
|
89
|
+
visit_collection(object.expressions)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def visit_Arel_Nodes_Case(object)
|
|
93
|
+
visit_collection([object.case, object.conditions, object.default])
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def visit_Arel_Nodes_And(object)
|
|
97
|
+
visit_Array(object.children)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def visit_Arel_Nodes_Node(object)
|
|
101
|
+
visit_default(object)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def visit_Arel_Attributes_Attribute(object)
|
|
105
|
+
visit_default(object)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def visit_default(_object)
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
module Nodes
|
|
114
|
+
class Binary < ::Arel::Nodes::Binary; end
|
|
115
|
+
class Grouping < ::Arel::Nodes::Grouping; end
|
|
116
|
+
|
|
117
|
+
::Arel::Visitors::ToSql.class_eval do
|
|
118
|
+
alias :visit_Mobility_Plugins_Arel_Nodes_Grouping :visit_Arel_Nodes_Grouping
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
register_plugin(:arel, Arel)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
module Mobility
|
|
3
|
+
module Plugins
|
|
4
|
+
module Arel
|
|
5
|
+
module Nodes
|
|
6
|
+
class Binary < ::Arel::Nodes::Binary; end
|
|
7
|
+
class Grouping < ::Arel::Nodes::Grouping; end
|
|
8
|
+
|
|
9
|
+
::Arel::Visitors::ToSql.class_eval do
|
|
10
|
+
alias :visit_Mobility_Plugins_Arel_Nodes_Grouping :visit_Arel_Nodes_Grouping
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
require "mobility/plugins/arel"
|
|
3
|
+
|
|
4
|
+
module Mobility
|
|
5
|
+
module Plugins
|
|
6
|
+
module Arel
|
|
7
|
+
module Nodes
|
|
8
|
+
%w[
|
|
9
|
+
JsonDashArrow
|
|
10
|
+
JsonDashDoubleArrow
|
|
11
|
+
JsonbDashArrow
|
|
12
|
+
JsonbDashDoubleArrow
|
|
13
|
+
JsonbQuestion
|
|
14
|
+
HstoreDashArrow
|
|
15
|
+
HstoreQuestion
|
|
16
|
+
].each do |name|
|
|
17
|
+
const_set name, (Class.new(Binary) do
|
|
18
|
+
include ::Arel::Predications
|
|
19
|
+
include ::Arel::OrderPredications
|
|
20
|
+
include ::Arel::AliasPredication
|
|
21
|
+
include MobilityExpressions
|
|
22
|
+
|
|
23
|
+
def lower
|
|
24
|
+
super self
|
|
25
|
+
end
|
|
26
|
+
end)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Needed for AR 4.2, can be removed when support is deprecated
|
|
30
|
+
if ::ActiveRecord::VERSION::STRING < '5.0'
|
|
31
|
+
[JsonbDashDoubleArrow, HstoreDashArrow].each do |klass|
|
|
32
|
+
klass.class_eval do
|
|
33
|
+
def quoted_node other
|
|
34
|
+
other && super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Jsonb < JsonbDashDoubleArrow
|
|
41
|
+
def to_dash_arrow
|
|
42
|
+
JsonbDashArrow.new left, right
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_question
|
|
46
|
+
JsonbQuestion.new left, right
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def eq other
|
|
50
|
+
case other
|
|
51
|
+
when NilClass
|
|
52
|
+
to_question.not
|
|
53
|
+
when Integer, Array, ::Hash
|
|
54
|
+
to_dash_arrow.eq other.to_json
|
|
55
|
+
when Jsonb
|
|
56
|
+
to_dash_arrow.eq other.to_dash_arrow
|
|
57
|
+
when JsonbDashArrow
|
|
58
|
+
to_dash_arrow.eq other
|
|
59
|
+
else
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Hstore < HstoreDashArrow
|
|
66
|
+
def to_question
|
|
67
|
+
HstoreQuestion.new left, right
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def eq other
|
|
71
|
+
other.nil? ? to_question.not : super
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Json < JsonDashDoubleArrow; end
|
|
76
|
+
|
|
77
|
+
class JsonContainer < Json
|
|
78
|
+
def initialize column, locale, attr
|
|
79
|
+
super(Nodes::JsonDashArrow.new(column, locale), attr)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class JsonbContainer < Jsonb
|
|
84
|
+
def initialize column, locale, attr
|
|
85
|
+
@column, @locale = column, locale
|
|
86
|
+
super(JsonbDashArrow.new(column, locale), attr)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def eq other
|
|
90
|
+
other.nil? ? super.or(JsonbQuestion.new(@column, @locale).not) : super
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
module Visitors
|
|
96
|
+
def visit_Mobility_Plugins_Arel_Nodes_JsonDashArrow o, a
|
|
97
|
+
json_infix o, a, '->'
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def visit_Mobility_Plugins_Arel_Nodes_JsonDashDoubleArrow o, a
|
|
101
|
+
json_infix o, a, '->>'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def visit_Mobility_Plugins_Arel_Nodes_JsonbDashArrow o, a
|
|
105
|
+
json_infix o, a, '->'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def visit_Mobility_Plugins_Arel_Nodes_JsonbDashDoubleArrow o, a
|
|
109
|
+
json_infix o, a, '->>'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def visit_Mobility_Plugins_Arel_Nodes_JsonbQuestion o, a
|
|
113
|
+
json_infix o, a, '?'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def visit_Mobility_Plugins_Arel_Nodes_HstoreDashArrow o, a
|
|
117
|
+
json_infix o, a, '->'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def visit_Mobility_Plugins_Arel_Nodes_HstoreQuestion o, a
|
|
121
|
+
json_infix o, a, '?'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def json_infix o, a, opr
|
|
127
|
+
visit(Nodes::Grouping.new(::Arel::Nodes::InfixOperation.new(opr, o.left, o.right)), a)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
::Arel::Visitors::PostgreSQL.include Visitors
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|