sunstone 2.0.4 → 5.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,28 +3,70 @@ module ActiveRecord
3
3
  module Sunstone
4
4
  module Type
5
5
  class Array < ActiveRecord::Type::Value
6
- include ActiveRecord::Type::Mutable
6
+ include ActiveRecord::Type::Helpers::Mutable
7
7
 
8
8
  attr_reader :subtype
9
- delegate :type, to: :subtype
9
+ delegate :type, :user_input_in_time_zone, :limit, to: :subtype
10
10
 
11
11
  def initialize(subtype)
12
12
  @subtype = subtype
13
13
  end
14
14
 
15
- def type_cast_for_database(value)
16
- super(value).to_json if value
15
+ def deserialize(value)
16
+ if value.is_a?(String)
17
+ type_cast_array(JSON.parse(value), :deserialize)
18
+ else
19
+ super
20
+ end
17
21
  end
18
22
 
19
- def cast_value(string)
20
- return string unless string.is_a?(::String)
21
- return if string.empty?
22
-
23
- JSON.parse(string)
23
+ def cast(value)
24
+ if value.is_a?(::String)
25
+ value = JSON.parse(value)
26
+ end
27
+ type_cast_array(value, :cast)
28
+ end
29
+
30
+ def serialize(value)
31
+ if value.is_a?(::Array)
32
+ type_cast_array(value, :serialize)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def ==(other)
39
+ other.is_a?(Array) && subtype == other.subtype
40
+ end
41
+
42
+ # def type_cast_for_schema(value)
43
+ # return super unless value.is_a?(::Array)
44
+ # "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
45
+ # end
46
+
47
+ def map(value, &block)
48
+ value.map(&block)
49
+ end
50
+
51
+ # def cast_value(string)
52
+ # return string unless string.is_a?(::String)
53
+ # return if string.empty?
54
+ #
55
+ # JSON.parse(string)
56
+ # end
57
+
58
+ private
59
+
60
+ def type_cast_array(value, method)
61
+ if value.is_a?(::Array)
62
+ value.map { |item| type_cast_array(item, method) }
63
+ else
64
+ @subtype.public_send(method, value)
65
+ end
24
66
  end
25
67
 
26
68
  end
27
69
  end
28
70
  end
29
71
  end
30
- end
72
+ end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module Type
5
5
  class DateTime < ActiveRecord::Type::DateTime
6
6
 
7
- def type_cast_for_database(value)
7
+ def serialize(value)
8
8
  super(value).iso8601(3) if value
9
9
  end
10
10
 
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  :ewkb
11
11
  end
12
12
 
13
- def type_cast_for_database(value)
13
+ def serialize(value)
14
14
  if value
15
15
  ::RGeo::WKRep::WKBGenerator.new(hex_format: true, type_format: :ewkb, emit_ewkb_srid: true).generate(value)
16
16
  end
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SunstoneSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
4
+ attr_reader :array
5
+
6
+ def initialize(type_metadata, options = {})
7
+ super(type_metadata)
8
+ @type_metadata = type_metadata
9
+ @primary_key = (options['primary_key'] == true)
10
+ @array = !!options['array']
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -34,7 +34,7 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  # Forward only valid config params to PGconn.connect.
37
- conn_params.keep_if { |k, _| VALID_SUNSTONE_CONN_PARAMS.include?(k) }
37
+ conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
38
38
 
39
39
  # The postgres drivers don't allow the creation of an unconnected PGconn object,
40
40
  # so just pass a nil connection object for the time being.
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
55
55
  # <encoding></tt> call on the connection.
56
56
  class SunstoneAPIAdapter < AbstractAdapter
57
- ADAPTER_NAME = 'Sunstone'
57
+ ADAPTER_NAME = 'Sunstone'.freeze
58
58
 
59
59
  NATIVE_DATABASE_TYPES = {
60
60
  string: { name: "string" },
@@ -63,8 +63,12 @@ module ActiveRecord
63
63
  boolean: { name: "boolean" }
64
64
  }
65
65
 
66
- include Sunstone::DatabaseStatements
66
+ # include PostgreSQL::Quoting
67
+ # include PostgreSQL::ReferentialIntegrity
67
68
  include Sunstone::SchemaStatements
69
+ include Sunstone::DatabaseStatements
70
+ # include PostgreSQL::ColumnDumper
71
+ # include Savepoints
68
72
 
69
73
  # Returns 'SunstoneAPI' as adapter name for identification purposes.
70
74
  def adapter_name
@@ -80,7 +84,7 @@ module ActiveRecord
80
84
 
81
85
  # Initializes and connects a SunstoneAPI adapter.
82
86
  def initialize(connection, logger, connection_parameters, config)
83
- super(connection, logger)
87
+ super(connection, logger, config)
84
88
 
85
89
  @visitor = Arel::Visitors::Sunstone.new
86
90
  @connection_parameters, @config = connection_parameters, config
@@ -149,6 +153,15 @@ module ActiveRecord
149
153
  def server_config
150
154
  Wankel.parse(@connection.get("/configuration").body)
151
155
  end
156
+
157
+ def lookup_cast_type_from_column(column) # :nodoc:
158
+ if column.array
159
+ Sunstone::Type::Array.new(type_map.lookup(column.sql_type))
160
+ else
161
+ type_map.lookup(column.sql_type)
162
+ end
163
+ end
164
+
152
165
 
153
166
  private
154
167
 
@@ -158,7 +171,7 @@ module ActiveRecord
158
171
  m.register_type 'integer', Type::Integer.new
159
172
  m.register_type 'decimal', Type::Decimal.new
160
173
  m.register_type 'datetime', Sunstone::Type::DateTime.new
161
- m.register_type 'hash', Type::Value.new
174
+ m.register_type 'json', Type::Internal::AbstractJson.new
162
175
  m.register_type 'ewkb', Sunstone::Type::EWKB.new
163
176
  end
164
177
 
@@ -186,6 +199,9 @@ module ActiveRecord
186
199
  def create_table_definition(name, temporary, options, as = nil) # :nodoc:
187
200
  SunstoneAPI::TableDefinition.new native_database_types, name, temporary, options, as
188
201
  end
202
+
203
+ ActiveRecord::Type.add_modifier({ array: true }, Sunstone::Type::Array, adapter: :sunstone)
204
+ # ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
189
205
  end
190
206
  end
191
207
  end
@@ -4,21 +4,41 @@ module Arel
4
4
 
5
5
  attr_accessor :request_type, :table, :where, :limit, :offset, :order, :operation, :columns, :updates, :eager_loads, :id
6
6
 
7
+ def cast_attribute(v)
8
+ if (v.is_a?(ActiveRecord::Attribute))
9
+ v.value_for_database
10
+ else
11
+ v
12
+ end
13
+ end
14
+
7
15
  def substitute_binds hash, bvs
8
16
  if hash.is_a?(Array)
9
- hash.map { |w| substitute_binds(w, bvs) }
10
- else
17
+ hash.map do |v|
18
+ if v.is_a?(Arel::Nodes::BindParam)
19
+ cast_attribute(bvs.last.is_a?(Array) ? bvs.shift.last : bvs.shift)
20
+ elsif v.is_a?(Hash) || v.is_a?(Array)
21
+ substitute_binds(v, bvs)
22
+ else
23
+ v
24
+ end
25
+ end
26
+ elsif hash.is_a?(Hash)
11
27
  newhash = {}
12
28
  hash.each do |k, v|
13
- if Arel::Nodes::BindParam === v
14
- newhash[k] = (bvs.last.is_a?(Array) ? bvs.shift.last : bvs.shift)
29
+ if v.is_a?(Arel::Nodes::BindParam)
30
+ newhash[k] = cast_attribute(bvs.last.is_a?(Array) ? bvs.shift.last : bvs.shift)
15
31
  elsif v.is_a?(Hash)
16
32
  newhash[k] = substitute_binds(v, bvs)
33
+ elsif v.is_a?(Array)
34
+ newhash[k] = substitute_binds(v, bvs)
17
35
  else
18
36
  newhash[k] = v
19
37
  end
20
38
  end
21
39
  newhash
40
+ else
41
+ cast_attribute(bvs.last.is_a?(Array) ? bvs.shift.last : bvs.shift)
22
42
  end
23
43
  end
24
44
 
@@ -55,9 +75,9 @@ module Arel
55
75
  get_params[:include] = eager_loads.clone
56
76
  end
57
77
 
58
- get_params[:limit] = limit if limit
59
- get_params[:offset] = offset if offset
60
- get_params[:order] = order if order
78
+ get_params[:limit] = substitute_binds(limit, bvs) if limit
79
+ get_params[:offset] = substitute_binds(offset, bvs) if offset
80
+ get_params[:order] = substitute_binds(order, bvs) if order
61
81
 
62
82
  case operation
63
83
  when :count
@@ -69,9 +89,9 @@ module Arel
69
89
  path += "/#{get_params[:where]['id']}"
70
90
  get_params.delete(:where)
71
91
  end
72
-
92
+
73
93
  if get_params.size > 0
74
- path += '?' + get_params.to_param
94
+ path += '?m=' + URI.escape(CGI.escape(MessagePack.pack(get_params)))
75
95
  end
76
96
 
77
97
  request = request_type.new(path)
@@ -18,8 +18,12 @@ module Arel
18
18
  accept(node, Arel::Collectors::SQLString.new, &block).value
19
19
  end
20
20
 
21
+ def preparable
22
+ false
23
+ end
24
+
21
25
  private
22
-
26
+
23
27
  def visit_Arel_Nodes_SelectStatement o, collector
24
28
  collector = o.cores.inject(collector) { |c,x|
25
29
  visit_Arel_Nodes_SelectCore(x, c)
@@ -64,17 +68,76 @@ module Arel
64
68
  collector
65
69
  end
66
70
 
71
+ def visit_Arel_Nodes_Overlaps o, collector
72
+ { visit(o.left, collector) => {overlaps: o.left.type_cast_for_database(o.right) }}
73
+ end
74
+
67
75
  def visit_Arel_Nodes_InsertStatement o, collector
68
76
  collector.request_type = Net::HTTP::Post
69
77
  collector.table = o.relation.name
70
78
  collector.operation = :insert
71
79
 
72
80
  if o.values
73
- collector.updates = o.values.right.map { |x| visit(x, collector) }.zip(o.values.left).to_h
81
+ keys = o.values.right.map { |x| visit(x, collector) }
82
+ values = o.values.left
83
+ collector.updates = {}
84
+
85
+
86
+ keys.each_with_index do |k, i|
87
+ if k.is_a?(Hash)
88
+ add_to_bottom_of_hash_or_array(k, values[i])
89
+ collector.updates.deep_merge!(k) { |key, v1, v2|
90
+ if (v1.is_a?(Array) && v2.is_a?(Array))
91
+ v2.each_with_index do |v, i|
92
+ if v1[i].nil?
93
+ v1[i] = v2[i]
94
+ else
95
+ v1[i].deep_merge!(v2[i]) unless v2[i].nil?
96
+ end
97
+ end
98
+ v1
99
+ else
100
+ v2
101
+ end
102
+ }
103
+ else
104
+ collector.updates[k] = values[i]
105
+ end
106
+ end
74
107
  end
75
108
 
76
109
  collector
77
110
  end
111
+
112
+ def find_bottom(hash)
113
+ if hash.is_a?(Hash)
114
+ if hash.values.first.is_a?(Array) || hash.values.first.is_a?(Hash)
115
+ find_bottom(hash.values.first)
116
+ else
117
+ hash
118
+ end
119
+ elsif hash.is_a?(Array)
120
+ fnn = hash.find { |i| !i.nil? }
121
+ if fnn.is_a?(Array) || fnn.is_a?(Hash)
122
+ fnn
123
+ else
124
+ hash
125
+ end
126
+ end
127
+ end
128
+
129
+ def add_to_bottom_of_hash_or_array(hash, value)
130
+ hash = find_bottom(hash)
131
+ if hash.is_a?(Hash)
132
+ nkey = hash.keys.first
133
+ nvalue = hash.values.first
134
+ hash[nkey] = { nvalue => value }
135
+ elsif hash.is_a?(Array)
136
+ fnni = hash.find_index { |i| !i.nil? }
137
+ nvalue = hash[fnni]
138
+ hash[fnni] = { nvalue => value }
139
+ end
140
+ end
78
141
 
79
142
 
80
143
  private
@@ -112,22 +175,50 @@ module Arel
112
175
  collector.request_type = Net::HTTP::Patch
113
176
  collector.table = o.relation.name
114
177
  collector.operation = :update
115
-
116
- wheres = o.wheres.map { |x| visit(x, collector) }.inject([]) { |c, w|
117
- w.is_a?(Array) ? c += w : c << w
118
- }
119
- if wheres.size != 1 && wheres.first.size != 1 && !wheres['id']
120
- raise 'Upsupported'
121
- else
122
- collector.where = wheres
178
+
179
+ # collector.id = o.wheres.first.children.first.right
180
+ if !o.wheres.empty?
181
+ collector.where = o.wheres.map { |x| visit(x, collector) }.inject([]) { |c, w|
182
+ w.is_a?(Array) ? c += w : c << w
183
+ }
123
184
  end
124
185
 
125
- collector.id = wheres.first['id']
186
+ if collector.where.size != 1 && collector.where.first.size != 1 && !collector.where.first['id']
187
+ raise 'Upsupported'
188
+ end
189
+ if !collector.where.first['id']
190
+ collector.table = collector.where.first.keys.first
191
+ collector.where[0] = {'id' => collector.where.first.values.first.values.first}
192
+ end
126
193
 
127
194
  if o.values
128
- collector.updates = o.values.map { |x| visit(x, collector) }.inject({}){|c,v| c.merge(v) }#.zip(o.values).to_h
195
+ collector.updates = {}
196
+
197
+ o.values.map { |x| visit(x, collector) }.each do |value|
198
+ value.each do |key, v|
199
+ if key.is_a?(Hash)
200
+ add_to_bottom_of_hash_or_array(key, v)
201
+ collector.updates.deep_merge!(key) { |key, v1, v2|
202
+ if (v1.is_a?(Array) && v2.is_a?(Array))
203
+ v2.each_with_index do |v, i|
204
+ if v1[i].nil?
205
+ v1[i] = v2[i]
206
+ else
207
+ v1[i].deep_merge!(v2[i]) unless v2[i].nil?
208
+ end
209
+ end
210
+ v1
211
+ else
212
+ v2
213
+ end
214
+ }
215
+ else
216
+ collector.updates[key] = v
217
+ end
218
+ end
219
+ end
129
220
  end
130
-
221
+
131
222
  collector
132
223
  end
133
224
  #
@@ -457,31 +548,63 @@ module Arel
457
548
  # collector << " BETWEEN "
458
549
  # visit o.right, collector
459
550
  # end
460
- #
461
- # def visit_Arel_Nodes_GreaterThanOrEqual o, collector
462
- # collector = visit o.left, collector
463
- # collector << " >= "
464
- # visit o.right, collector
465
- # end
466
- #
551
+
552
+ def visit_Arel_Nodes_GreaterThanOrEqual o, collector
553
+ key = visit(o.left, collector)
554
+ value = { :gte => visit(o.right, collector) }
555
+ if key.is_a?(Hash)
556
+ if o.left.is_a?(Arel::Attributes::Cast)
557
+ merge_to_bottom_hash(key, value)
558
+ else
559
+ add_to_bottom_of_hash(key, value)
560
+ end
561
+ else
562
+ { key => value }
563
+ end
564
+ end
565
+
467
566
  def visit_Arel_Nodes_GreaterThan o, collector
468
- {
469
- visit(o.left, collector) => { :greater_than => visit(o.right, collector) }
470
- }
567
+ key = visit(o.left, collector)
568
+ value = { :gt => visit(o.right, collector) }
569
+ if key.is_a?(Hash)
570
+ if o.left.is_a?(Arel::Attributes::Cast)
571
+ merge_to_bottom_hash(key, value)
572
+ else
573
+ add_to_bottom_of_hash(key, value)
574
+ end
575
+ else
576
+ { key => value }
577
+ end
578
+ end
579
+
580
+ def visit_Arel_Nodes_LessThanOrEqual o, collector
581
+ key = visit(o.left, collector)
582
+ value = { :lte => visit(o.right, collector) }
583
+ if key.is_a?(Hash)
584
+ if o.left.is_a?(Arel::Attributes::Cast)
585
+ merge_to_bottom_hash(key, value)
586
+ else
587
+ add_to_bottom_of_hash(key, value)
588
+ end
589
+ else
590
+ { key => value }
591
+ end
592
+ end
593
+
594
+ def visit_Arel_Nodes_LessThan o, collector
595
+ key = visit(o.left, collector)
596
+ value = { :lte => visit(o.right, collector) }
597
+ if key.is_a?(Hash)
598
+ if o.left.is_a?(Arel::Attributes::Cast)
599
+ merge_to_bottom_hash(key, value)
600
+ else
601
+ add_to_bottom_of_hash(key, value)
602
+ end
603
+ else
604
+ { key => value }
605
+ end
471
606
  end
472
607
 
473
- # def visit_Arel_Nodes_LessThanOrEqual o, collector
474
- # collector = visit o.left, collector
475
- # collector << " <= "
476
- # visit o.right, collector
477
- # end
478
- #
479
- # def visit_Arel_Nodes_LessThan o, collector
480
- # collector = visit o.left, collector
481
- # collector << " < "
482
- # visit o.right, collector
483
- # end
484
- #
485
608
  # def visit_Arel_Nodes_Matches o, collector
486
609
  # collector = visit o.left, collector
487
610
  # collector << " LIKE "
@@ -561,15 +684,11 @@ module Arel
561
684
  end
562
685
  end
563
686
 
564
- # def visit_Arel_Nodes_In o, collector
565
- # if Array === o.right && o.right.empty?
566
- # collector << '1=0'
567
- # else
568
- # collector = visit o.left, collector
569
- # collector << " IN ("
570
- # visit(o.right, collector) << ")"
571
- # end
572
- # end
687
+ def visit_Arel_Nodes_In o, collector
688
+ {
689
+ visit(o.left, collector) => {in: visit(o.right, collector)}
690
+ }
691
+ end
573
692
  #
574
693
  # def visit_Arel_Nodes_NotIn o, collector
575
694
  # if Array === o.right && o.right.empty?
@@ -587,10 +706,11 @@ module Arel
587
706
  ors << o.children.inject({}) do |c, x|
588
707
  value = visit(x, collector)
589
708
  if value.is_a?(Hash)
590
- c.deep_merge(value)
709
+ c.deep_merge!(value)
591
710
  elsif value.is_a?(Array)
592
711
  value.size == 1 ? ors << value : ors += value
593
712
  end
713
+ c
594
714
  end
595
715
  ors
596
716
  end
@@ -605,8 +725,10 @@ module Arel
605
725
  end
606
726
 
607
727
  def visit_Arel_Nodes_Assignment o, collector
608
- case o.right
609
- when Arel::Nodes::UnqualifiedColumn, Arel::Attributes::Attribute, Arel::Nodes::BindParam
728
+ case o.left
729
+ when Arel::Nodes::UnqualifiedColumn
730
+ { visit(o.left.expr, collector) => visit(o.right, collector) }
731
+ when Arel::Attributes::Attribute, Arel::Nodes::BindParam
610
732
  { visit(o.left, collector) => visit(o.right, collector) }
611
733
  else
612
734
  collector = visit o.left, collector
@@ -615,20 +737,91 @@ module Arel
615
737
  end
616
738
  end
617
739
 
740
+ def merge_to_bottom_hash(hash, value)
741
+ okey = hash
742
+ while okey.values.first.is_a?(Hash)
743
+ okey = okey.values.first
744
+ end
745
+ okey.merge!(value)
746
+ hash
747
+ end
748
+
749
+ def add_to_bottom_of_hash(hash, value)
750
+ okey = hash
751
+ while okey.is_a?(Hash) && (okey.values.first.is_a?(Hash) || okey.values.first.is_a?(Array))
752
+ if okey.is_a?(Array)
753
+ okey = okey.find { |i| !i.nil? }
754
+ else
755
+ okey = okey.values.first
756
+ end
757
+ end
758
+ nkey = okey.keys.first
759
+ nvalue = okey.values.first
760
+ okey[nkey] = { nvalue => value }
761
+ hash
762
+ end
763
+
618
764
  def visit_Arel_Nodes_Equality o, collector
619
- key = visit(o.left, collector).to_s.split('.')
765
+ key = visit(o.left, collector)
620
766
  value = (o.right.nil? ? nil : visit(o.right, collector))
621
767
 
622
- hash = {
623
- key.pop => value
624
- }
625
-
626
- while key.size > 0
627
- hash = { key.pop => hash }
768
+ if key.is_a?(Hash)
769
+ add_to_bottom_of_hash(key, {eq: value})
770
+ else
771
+ key = key.to_s.split('.')
772
+ hash = { key.pop => value }
773
+ while key.size > 0
774
+ hash = { key.pop => hash }
775
+ end
776
+ hash
628
777
  end
629
- hash
630
778
  end
779
+
780
+ def visit_Arel_Nodes_TSMatch(o, collector)
781
+ key = visit(o.left, collector)
782
+ value = { ts_match: (o.right.nil? ? nil : visit(o.right, collector)) }
631
783
 
784
+ if key.is_a?(Hash)
785
+ add_to_bottom_of_hash(key, value)
786
+ else
787
+ key = key.to_s.split('.')
788
+ hash = { key.pop => value }
789
+ while key.size > 0
790
+ hash = { key.pop => hash }
791
+ end
792
+ hash
793
+ end
794
+ end
795
+
796
+ def visit_Arel_Nodes_TSVector(o, collector)
797
+ visit(o.attribute, collector)
798
+ end
799
+
800
+ def visit_Arel_Nodes_TSQuery(o, collector)
801
+ if o.language
802
+ [visit(o.expression, collector), visit(o.language, collector)]
803
+ else
804
+ visit(o.expression, collector)
805
+ end
806
+ end
807
+
808
+ def visit_Arel_Nodes_HasKey o, collector
809
+ key = visit(o.left, collector)
810
+ value = {has_key: (o.right.nil? ? nil : o.right.to_s)}
811
+
812
+ if key.is_a?(Hash)
813
+ okey = key
814
+ while okey.values.first.is_a?(Hash)
815
+ okey = okey.values.first
816
+ end
817
+ nkey = okey.keys.first
818
+ nvalue = okey.values.first
819
+ okey[nkey] = { nvalue => value }
820
+ else
821
+ { key => value }
822
+ end
823
+ end
824
+
632
825
  def visit_Arel_Nodes_NotEqual o, collector
633
826
  {
634
827
  visit(o.left, collector) => { :not => visit(o.right, collector) }
@@ -645,11 +838,47 @@ module Arel
645
838
  def visit_Arel_Nodes_UnqualifiedColumn o, collector
646
839
  o.name
647
840
  end
841
+
842
+ def visit_Arel_Attributes_Cast(o, collector)
843
+ key = visit(o.relation, collector)
844
+ value = { :cast => o.name }
845
+ if key.is_a?(Hash)
846
+ add_to_bottom_of_hash(key, value)
847
+ else
848
+ { key => value }
849
+ end
850
+ end
851
+
852
+ def visit_Arel_Attributes_Key o, collector
853
+ key = visit(o.relation, collector)
854
+ if key.is_a?(Hash)
855
+ okey = key
856
+ while okey.values.first.is_a?(Hash)
857
+ okey = okey.values.first
858
+ end
859
+ nkey = okey.keys.first
860
+ value = okey.values.first
861
+ okey[nkey] = {value => o.name}
862
+ key
863
+ else
864
+ { key => o.name }
865
+ end
866
+ end
648
867
 
868
+ def visit_Arel_Attributes_Relation o, collector
869
+ if o.collection
870
+ ary = []
871
+ ary[o.collection] = visit(o.relation, collector).to_s.split('.').last
872
+ {"#{o.name}_attributes" => ary}
873
+ else
874
+ {"#{o.name}_attributes" => visit(o.relation, collector).to_s.split('.').last}
875
+ end
876
+ end
877
+
649
878
  def visit_Arel_Attributes_Attribute o, collector
650
879
  join_name = o.relation.table_alias || o.relation.name
651
- # collector <<
652
880
  collector.table == join_name ? o.name : "#{join_name}.#{o.name}"
881
+ # o.name
653
882
  end
654
883
  alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
655
884
  alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute