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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -2
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +63 -0
  5. data/Gemfile +5 -2
  6. data/Gemfile.lock +39 -20
  7. data/README.md +183 -93
  8. data/lib/mobility.rb +101 -169
  9. data/lib/mobility/backend.rb +27 -51
  10. data/lib/mobility/backends.rb +20 -0
  11. data/lib/mobility/backends/active_record.rb +4 -0
  12. data/lib/mobility/backends/active_record/column.rb +3 -1
  13. data/lib/mobility/backends/active_record/container.rb +10 -11
  14. data/lib/mobility/backends/active_record/hstore.rb +6 -4
  15. data/lib/mobility/backends/active_record/json.rb +5 -3
  16. data/lib/mobility/backends/active_record/jsonb.rb +5 -3
  17. data/lib/mobility/backends/active_record/key_value.rb +31 -13
  18. data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
  19. data/lib/mobility/backends/active_record/serialized.rb +6 -0
  20. data/lib/mobility/backends/active_record/table.rb +17 -10
  21. data/lib/mobility/backends/column.rb +0 -6
  22. data/lib/mobility/backends/container.rb +10 -1
  23. data/lib/mobility/backends/hash.rb +39 -0
  24. data/lib/mobility/backends/hash_valued.rb +4 -0
  25. data/lib/mobility/backends/hstore.rb +0 -1
  26. data/lib/mobility/backends/json.rb +0 -1
  27. data/lib/mobility/backends/jsonb.rb +1 -2
  28. data/lib/mobility/backends/key_value.rb +31 -26
  29. data/lib/mobility/backends/null.rb +2 -0
  30. data/lib/mobility/backends/sequel.rb +37 -2
  31. data/lib/mobility/backends/sequel/column.rb +2 -0
  32. data/lib/mobility/backends/sequel/container.rb +11 -9
  33. data/lib/mobility/backends/sequel/hstore.rb +3 -1
  34. data/lib/mobility/backends/sequel/json.rb +3 -0
  35. data/lib/mobility/backends/sequel/jsonb.rb +3 -1
  36. data/lib/mobility/backends/sequel/key_value.rb +87 -18
  37. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  38. data/lib/mobility/backends/sequel/serialized.rb +6 -0
  39. data/lib/mobility/backends/sequel/table.rb +22 -9
  40. data/lib/mobility/backends/serialized.rb +1 -3
  41. data/lib/mobility/backends/table.rb +39 -31
  42. data/lib/mobility/pluggable.rb +56 -0
  43. data/lib/mobility/plugin.rb +260 -0
  44. data/lib/mobility/plugins.rb +27 -24
  45. data/lib/mobility/plugins/active_model.rb +17 -0
  46. data/lib/mobility/plugins/active_model/cache.rb +26 -0
  47. data/lib/mobility/plugins/active_model/dirty.rb +119 -78
  48. data/lib/mobility/plugins/active_record.rb +37 -0
  49. data/lib/mobility/plugins/active_record/backend.rb +27 -0
  50. data/lib/mobility/plugins/active_record/cache.rb +28 -0
  51. data/lib/mobility/plugins/active_record/dirty.rb +34 -17
  52. data/lib/mobility/plugins/active_record/query.rb +43 -31
  53. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +64 -0
  54. data/lib/mobility/plugins/arel.rb +125 -0
  55. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  56. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  57. data/lib/mobility/plugins/attribute_methods.rb +29 -20
  58. data/lib/mobility/plugins/attributes.rb +72 -0
  59. data/lib/mobility/plugins/backend.rb +161 -0
  60. data/lib/mobility/plugins/backend_reader.rb +34 -0
  61. data/lib/mobility/plugins/cache.rb +68 -26
  62. data/lib/mobility/plugins/default.rb +22 -17
  63. data/lib/mobility/plugins/dirty.rb +12 -33
  64. data/lib/mobility/plugins/fallbacks.rb +52 -44
  65. data/lib/mobility/plugins/fallthrough_accessors.rb +19 -23
  66. data/lib/mobility/plugins/locale_accessors.rb +22 -35
  67. data/lib/mobility/plugins/presence.rb +28 -21
  68. data/lib/mobility/plugins/query.rb +8 -17
  69. data/lib/mobility/plugins/reader.rb +50 -0
  70. data/lib/mobility/plugins/sequel.rb +34 -0
  71. data/lib/mobility/plugins/sequel/backend.rb +25 -0
  72. data/lib/mobility/plugins/sequel/cache.rb +24 -0
  73. data/lib/mobility/plugins/sequel/dirty.rb +34 -23
  74. data/lib/mobility/plugins/sequel/query.rb +21 -6
  75. data/lib/mobility/plugins/writer.rb +44 -0
  76. data/lib/mobility/translations.rb +95 -0
  77. data/lib/mobility/version.rb +12 -1
  78. data/lib/rails/generators/mobility/templates/create_string_translations.rb +0 -1
  79. data/lib/rails/generators/mobility/templates/create_text_translations.rb +0 -1
  80. data/lib/rails/generators/mobility/templates/initializer.rb +104 -78
  81. metadata +35 -40
  82. metadata.gz.sig +0 -0
  83. data/lib/mobility/active_model.rb +0 -4
  84. data/lib/mobility/active_model/backend_resetter.rb +0 -26
  85. data/lib/mobility/active_record.rb +0 -23
  86. data/lib/mobility/active_record/backend_resetter.rb +0 -26
  87. data/lib/mobility/active_record/model_translation.rb +0 -14
  88. data/lib/mobility/active_record/string_translation.rb +0 -10
  89. data/lib/mobility/active_record/text_translation.rb +0 -10
  90. data/lib/mobility/active_record/translation.rb +0 -14
  91. data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
  92. data/lib/mobility/arel.rb +0 -49
  93. data/lib/mobility/arel/nodes.rb +0 -13
  94. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  95. data/lib/mobility/arel/visitor.rb +0 -61
  96. data/lib/mobility/attributes.rb +0 -324
  97. data/lib/mobility/backend/orm_delegator.rb +0 -44
  98. data/lib/mobility/backend_resetter.rb +0 -50
  99. data/lib/mobility/configuration.rb +0 -138
  100. data/lib/mobility/fallbacks.rb +0 -28
  101. data/lib/mobility/interface.rb +0 -0
  102. data/lib/mobility/loaded.rb +0 -4
  103. data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
  104. data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
  105. data/lib/mobility/sequel.rb +0 -9
  106. data/lib/mobility/sequel/backend_resetter.rb +0 -23
  107. data/lib/mobility/sequel/column_changes.rb +0 -28
  108. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  109. data/lib/mobility/sequel/model_translation.rb +0 -20
  110. data/lib/mobility/sequel/sql.rb +0 -16
  111. data/lib/mobility/sequel/string_translation.rb +0 -10
  112. data/lib/mobility/sequel/text_translation.rb +0 -10
  113. data/lib/mobility/sequel/translation.rb +0 -53
  114. 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,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