mobility 0.8.11 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +69 -1
  5. data/Gemfile +50 -18
  6. data/Gemfile.lock +32 -75
  7. data/README.md +184 -92
  8. data/Rakefile +6 -4
  9. data/lib/mobility.rb +100 -168
  10. data/lib/mobility/backend.rb +27 -51
  11. data/lib/mobility/backends.rb +20 -0
  12. data/lib/mobility/backends/active_record.rb +4 -0
  13. data/lib/mobility/backends/active_record/column.rb +3 -1
  14. data/lib/mobility/backends/active_record/container.rb +10 -11
  15. data/lib/mobility/backends/active_record/hstore.rb +6 -4
  16. data/lib/mobility/backends/active_record/json.rb +5 -3
  17. data/lib/mobility/backends/active_record/jsonb.rb +5 -3
  18. data/lib/mobility/backends/active_record/key_value.rb +31 -13
  19. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  20. data/lib/mobility/backends/active_record/serialized.rb +6 -0
  21. data/lib/mobility/backends/active_record/table.rb +17 -10
  22. data/lib/mobility/backends/column.rb +0 -6
  23. data/lib/mobility/backends/container.rb +10 -1
  24. data/lib/mobility/backends/hash.rb +39 -0
  25. data/lib/mobility/backends/hash_valued.rb +4 -0
  26. data/lib/mobility/backends/hstore.rb +0 -1
  27. data/lib/mobility/backends/json.rb +0 -1
  28. data/lib/mobility/backends/jsonb.rb +1 -2
  29. data/lib/mobility/backends/key_value.rb +31 -26
  30. data/lib/mobility/backends/null.rb +2 -0
  31. data/lib/mobility/backends/sequel.rb +37 -2
  32. data/lib/mobility/backends/sequel/column.rb +2 -0
  33. data/lib/mobility/backends/sequel/container.rb +11 -9
  34. data/lib/mobility/backends/sequel/hstore.rb +3 -1
  35. data/lib/mobility/backends/sequel/json.rb +3 -0
  36. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  37. data/lib/mobility/backends/sequel/key_value.rb +87 -18
  38. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  39. data/lib/mobility/backends/sequel/serialized.rb +6 -0
  40. data/lib/mobility/backends/sequel/table.rb +22 -9
  41. data/lib/mobility/backends/serialized.rb +1 -3
  42. data/lib/mobility/backends/table.rb +39 -31
  43. data/lib/mobility/pluggable.rb +56 -0
  44. data/lib/mobility/plugin.rb +260 -0
  45. data/lib/mobility/plugins.rb +27 -24
  46. data/lib/mobility/plugins/active_model.rb +17 -0
  47. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  48. data/lib/mobility/plugins/active_model/dirty.rb +119 -78
  49. data/lib/mobility/plugins/active_record.rb +37 -0
  50. data/lib/mobility/plugins/active_record/backend.rb +27 -0
  51. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  52. data/lib/mobility/plugins/active_record/dirty.rb +34 -17
  53. data/lib/mobility/plugins/active_record/query.rb +43 -31
  54. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
  55. data/lib/mobility/plugins/arel.rb +125 -0
  56. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  57. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  58. data/lib/mobility/plugins/attribute_methods.rb +29 -20
  59. data/lib/mobility/plugins/attributes.rb +72 -0
  60. data/lib/mobility/plugins/backend.rb +161 -0
  61. data/lib/mobility/plugins/backend_reader.rb +34 -0
  62. data/lib/mobility/plugins/cache.rb +68 -26
  63. data/lib/mobility/plugins/default.rb +22 -17
  64. data/lib/mobility/plugins/dirty.rb +12 -33
  65. data/lib/mobility/plugins/fallbacks.rb +52 -44
  66. data/lib/mobility/plugins/fallthrough_accessors.rb +25 -25
  67. data/lib/mobility/plugins/locale_accessors.rb +22 -35
  68. data/lib/mobility/plugins/presence.rb +28 -21
  69. data/lib/mobility/plugins/query.rb +8 -17
  70. data/lib/mobility/plugins/reader.rb +50 -0
  71. data/lib/mobility/plugins/sequel.rb +34 -0
  72. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  73. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  74. data/lib/mobility/plugins/sequel/dirty.rb +34 -23
  75. data/lib/mobility/plugins/sequel/query.rb +21 -6
  76. data/lib/mobility/plugins/writer.rb +44 -0
  77. data/lib/mobility/translations.rb +95 -0
  78. data/lib/mobility/version.rb +12 -1
  79. data/lib/rails/generators/mobility/templates/initializer.rb +96 -78
  80. metadata +31 -42
  81. metadata.gz.sig +0 -0
  82. data/lib/mobility/active_model.rb +0 -4
  83. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  84. data/lib/mobility/active_record.rb +0 -23
  85. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  86. data/lib/mobility/active_record/model_translation.rb +0 -14
  87. data/lib/mobility/active_record/string_translation.rb +0 -10
  88. data/lib/mobility/active_record/text_translation.rb +0 -10
  89. data/lib/mobility/active_record/translation.rb +0 -14
  90. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  91. data/lib/mobility/arel.rb +0 -49
  92. data/lib/mobility/arel/nodes.rb +0 -13
  93. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  94. data/lib/mobility/arel/visitor.rb +0 -61
  95. data/lib/mobility/attributes.rb +0 -324
  96. data/lib/mobility/backend/orm_delegator.rb +0 -44
  97. data/lib/mobility/backend_resetter.rb +0 -50
  98. data/lib/mobility/configuration.rb +0 -138
  99. data/lib/mobility/fallbacks.rb +0 -28
  100. data/lib/mobility/interface.rb +0 -0
  101. data/lib/mobility/loaded.rb +0 -4
  102. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  103. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  104. data/lib/mobility/sequel.rb +0 -9
  105. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  106. data/lib/mobility/sequel/column_changes.rb +0 -28
  107. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  108. data/lib/mobility/sequel/model_translation.rb +0 -20
  109. data/lib/mobility/sequel/sql.rb +0 -16
  110. data/lib/mobility/sequel/string_translation.rb +0 -10
  111. data/lib/mobility/sequel/text_translation.rb +0 -10
  112. data/lib/mobility/sequel/translation.rb +0 -53
  113. 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
- class MethodsBuilder < ActiveModel::Dirty::MethodsBuilder
44
- # @param [Attributes] attributes
45
- def included(model_class)
46
- super
43
+ extend Plugin
47
44
 
48
- model_class.include InstanceMethods
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
- class << self
52
- def dirty_class
53
- @dirty_class ||= (Class.new do
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
- class << self
19
- def apply(attributes)
20
- attributes.model_class.class_eval do
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(*attributes.names)
23
- singleton_class.send :alias_method, Mobility.query_method, :__mobility_query_scope__
31
+ extend FindByMethods.new(*plugin.names)
32
+ singleton_class.send :alias_method, plugin.query_method, :__mobility_query_scope__
24
33
  end
25
- attributes.backend_class.include self
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
- # @note We use +instance_variable_get+ here to get the +AttributeSet+
34
- # rather than the hash of attributes. Getting the full hash of
35
- # attributes is a performance hit and better to avoid if unnecessary.
36
- # TODO: Improve this.
37
- def read(locale, **)
38
- if (model_attributes_defined? &&
39
- model_attributes.key?(alias_ = Query.attribute_alias(attribute, locale)))
40
- model_attributes[alias_].value
41
- else
42
- super
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 unless scope.respond_to?(:mobility_modules)
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 = scope.mobility_modules.inject(IDENTITY) do |qm, mod|
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(&:and))]
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,60 @@
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
+ end
14
+ end
15
+ end
16
+
17
+ class UniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
18
+ # @param [ActiveRecord::Base] record Translated model
19
+ # @param [String] attribute Name of attribute
20
+ # @param [Object] value Attribute value
21
+ def validate_each(record, attribute, value)
22
+ klass = record.class
23
+
24
+ if ([*options[:scope]] + [attribute]).any? { |name| klass.mobility_attribute?(name) }
25
+ return unless value.present?
26
+ relation = klass.unscoped.__mobility_query_scope__ do |m|
27
+ node = m.__send__(attribute)
28
+ options[:case_sensitive] == false ? node.lower.eq(value.downcase) : node.eq(value)
29
+ end
30
+ relation = relation.where.not(klass.primary_key => record.id) if record.persisted?
31
+ relation = mobility_scope_relation(record, relation)
32
+ relation = relation.merge(options[:conditions]) if options[:conditions]
33
+
34
+ if relation.exists?
35
+ error_options = options.except(:case_sensitive, :scope, :conditions)
36
+ error_options[:value] = value
37
+
38
+ record.errors.add(attribute, :taken, **error_options)
39
+ end
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def mobility_scope_relation(record, relation)
48
+ [*options[:scope]].inject(relation) do |scoped_relation, scope_item|
49
+ scoped_relation.__mobility_query_scope__ do |m|
50
+ m.__send__(scope_item).eq(record.send(scope_item))
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ register_plugin(:active_record_uniqueness_validation, ActiveRecord::UniquenessValidation)
59
+ end
60
+ 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