ar_jdbc_pg_array 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ atcbd = ActiveRecord::AttributeMethods::Read::ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
2
+
3
+ atcbd << /_array$/
4
+ def atcbd.include?(val)
5
+ any?{|type| type === val}
6
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module Dirty
3
+ private
4
+ def attribute_will_change!(attr)
5
+ val = changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
6
+ if Array === val && !(Array === @attributes[attr])
7
+ send(attr) unless @attributes_cache.has_key?(attr)
8
+ @attributes[attr] = @attributes_cache[attr]
9
+ end
10
+ val
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class Bulk < ActiveRecord::Base
2
+ end
3
+
4
+ class Bulk1 < Bulk
5
+ end
@@ -0,0 +1,36 @@
1
+ first:
2
+ id: 1
3
+ ints: [ 1 ]
4
+ strings: [ one ]
5
+ times: [2011-03-01, 2011-05-05]
6
+ floats: [1.0, 2.3]
7
+ decimals: [1.0, 2.3]
8
+ second:
9
+ id: 2
10
+ ints: [ 2 ]
11
+ strings: [ two ]
12
+ times: [2010-01-01, 2010-02-01]
13
+ floats: [2.0, 2.3]
14
+ decimals: [2.0, 2.3]
15
+ third:
16
+ id: 3
17
+ ints: [ 2, 3 ]
18
+ strings: [ two, three ]
19
+ times: [2010-03-01, 2010-04-01]
20
+ floats: [2.0, 2.5]
21
+ decimals: [2.0, 2.5]
22
+ fourth:
23
+ id: 4
24
+ ints: [ ]
25
+ strings: [ ]
26
+ times: [ ]
27
+ floats: [ ]
28
+ decimals: [ ]
29
+ fifth:
30
+ id: 5
31
+ type: "Bulk1"
32
+ ints: [ 10 ]
33
+ strings: [ "#{1+1}", "\\#{1+1}\"'\\z\x01", "\t\n" ]
34
+ times: [ ]
35
+ floats: [ ]
36
+ decimals: [ ]
@@ -0,0 +1,4 @@
1
+ class Item < ActiveRecord::Base
2
+ references_by_array :tags, :validate=>true
3
+ serialize :for_yaml
4
+ end
@@ -0,0 +1,22 @@
1
+ i1:
2
+ id: 1
3
+ tag_ids: [ 1 ]
4
+ i2:
5
+ id: 2
6
+ tag_ids: [ 3 ]
7
+ i3:
8
+ id: 3
9
+ tag_ids: [ 1, 3 ]
10
+ i4:
11
+ id: 4
12
+ tag_ids: [ 3, 1 ]
13
+ i5:
14
+ id: 5
15
+ tag_ids: [ 1, 2 ]
16
+ i6:
17
+ id: 6
18
+ tag_ids: [ 1, 2, 3 ]
19
+ i7:
20
+ id: 7
21
+ tag_ids: [ ]
22
+
@@ -0,0 +1,30 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table "tags", :force => true do |t|
3
+ t.string :name
4
+ t.timestamps
5
+ end
6
+
7
+ create_table "items", :force => true do |t|
8
+ t.string :value
9
+ t.integer_array :tag_ids, :default => [1, 2]
10
+ t.string_array :tag_names, :default => %w{as so}
11
+ t.text :for_yaml
12
+ end
13
+
14
+ create_table "bulks", :force => true do |t|
15
+ t.string :type, :default => "Bulk"
16
+ t.string :value, :default => "'"
17
+ t.integer_array :ints, :default => [1, 2]
18
+ t.string_array :strings, :default => %w{as so}
19
+ t.timestamp_array :times, :default => %w{2010-01-01 2010-02-01}
20
+ t.float_array :floats, :default => [1.0, 1.2]
21
+ t.decimal_array :decimals, :default => [1.0, 1.2]
22
+ t.text_array :texts, :default => [nil, 'Text', 'NULL', 'Text with nil', 'Text with , nil, !"\\', 'nil']
23
+ t.integer_array :empty_def, :default => []
24
+ end
25
+
26
+ create_table "unrelateds", :force => true do |t|
27
+ t.text :for_yaml
28
+ t.text :for_custom_serialize
29
+ end
30
+ end
@@ -0,0 +1,2 @@
1
+ class Tag < ActiveRecord::Base
2
+ end
@@ -0,0 +1,15 @@
1
+ one:
2
+ id: 1
3
+ name: one
4
+ created_at: <%= Date.today.to_s(:db) %>
5
+ updated_at: <%= Time.now.to_s(:db) %>
6
+ two:
7
+ id: 2
8
+ name: two
9
+ created_at: <%= Date.today.to_s(:db) %>
10
+ updated_at: <%= Time.now.to_s(:db) %>
11
+ three:
12
+ id: 3
13
+ name: three
14
+ created_at: <%= Date.today.to_s(:db) %>
15
+ updated_at: <%= Time.now.to_s(:db) %>
@@ -0,0 +1,21 @@
1
+ require 'base64'
2
+ class Unrelated < ActiveRecord::Base
3
+ class MySerializer
4
+ def initialize(val)
5
+ @val = val
6
+ end
7
+
8
+ def self.dump(obj)
9
+ Base64.encode64(Marshal.dump(obj))
10
+ end
11
+
12
+ def self.load(str)
13
+ if str
14
+ Marshal.load(Base64.decode64(str))
15
+ end
16
+ end
17
+ end
18
+
19
+ serialize :for_yaml
20
+ serialize :for_custom_serialize, MySerializer
21
+ end
@@ -0,0 +1,3 @@
1
+ item1:
2
+ id: 1
3
+ for_yaml: {"a": "b"}
@@ -0,0 +1,380 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "PgArray" do
4
+ context "Array" do
5
+ before :all do
6
+ @ability_class = Class.new do
7
+ include CanCan::Ability
8
+ end
9
+ @acheck = Struct.new(:a)
10
+ end
11
+
12
+ before :each do
13
+ @ability = @ability_class.new
14
+ end
15
+
16
+ it "should change type" do
17
+ [].pg.should be_an_instance_of(PGArrays::PgArray)
18
+ [].search_any.should be_an_instance_of(PGArrays::PgAny)
19
+ [].search_all.should be_an_instance_of(PGArrays::PgAll)
20
+ [].search_subarray.should be_an_instance_of(PGArrays::PgIncludes)
21
+ end
22
+
23
+ it "should provide search_any for cancan" do
24
+ ab.can :boom, @acheck, :a => [1, 2].search_any
25
+ ab.should be_able_to(:boom, the([1]))
26
+ ab.should be_able_to(:boom, the([1, 3]))
27
+ ab.should be_able_to(:boom, the([3, 1]))
28
+ ab.should be_able_to(:boom, the([1, 2]))
29
+ ab.should be_able_to(:boom, the([1, 2, 3]))
30
+ ab.should_not be_able_to(:boom, the([3]))
31
+ ab.should_not be_able_to(:boom, the([]))
32
+ end
33
+
34
+ it "should provide search_all for cancan" do
35
+ ab.can :boom, @acheck, :a => [1, 2].search_all
36
+ ab.should_not be_able_to(:boom, the([1]))
37
+ ab.should_not be_able_to(:boom, the([1, 3]))
38
+ ab.should_not be_able_to(:boom, the([3, 1]))
39
+ ab.should be_able_to(:boom, the([1, 2]))
40
+ ab.should be_able_to(:boom, the([1, 2, 3]))
41
+ ab.should_not be_able_to(:boom, the([3]))
42
+ ab.should_not be_able_to(:boom, the([]))
43
+ end
44
+
45
+ it "should provide search_subarray for cancan" do
46
+ ab.can :boom, @acheck, :a => [1, 2].search_subarray
47
+ ab.should be_able_to(:boom, the([1]))
48
+ ab.should_not be_able_to(:boom, the([1, 3]))
49
+ ab.should_not be_able_to(:boom, the([3, 1]))
50
+ ab.should be_able_to(:boom, the([1, 2]))
51
+ ab.should_not be_able_to(:boom, the([1, 2, 3]))
52
+ ab.should_not be_able_to(:boom, the([3]))
53
+ ab.should be_able_to(:boom, the([]))
54
+ end
55
+
56
+ def the(ar)
57
+ @acheck.new(ar)
58
+ end
59
+
60
+ def ab
61
+ @ability
62
+ end
63
+ end
64
+
65
+ context "AR" do
66
+ it "should adequatly insert fixtures" do
67
+ bulk = Bulk.find(1)
68
+ bulk.ints.should == [ 1 ]
69
+ bulk.strings.should == %w{one}
70
+ map_times(bulk.times).should ==
71
+ map_times(parse_times(%w{2011-03-01 2011-05-05}))
72
+ bulk.floats.should == [1.0, 2.3]
73
+ bulk.decimals.should == [1.0, 2.3]
74
+ end
75
+
76
+ it "should be created with defaults" do
77
+ bulk = Bulk.new
78
+ bulk.ints.should == [1, 2]
79
+ bulk.strings.should == %w{as so}
80
+ bulk.floats.should == [1.0, 1.2]
81
+ bulk.decimals.should == [1.0, 1.2]
82
+ bulk.texts.should == [nil, 'Text', 'NULL', 'Text with nil', 'Text with , nil, !"\\', 'nil']
83
+ map_times(bulk.times).should ==
84
+ map_times(parse_times(%w{2010-01-01 2010-02-01}))
85
+ bulk.empty_def.should == []
86
+ end
87
+
88
+ it "should be able to insert" do
89
+ bulki = Bulk.new
90
+ bulki.save
91
+ bulk = Bulk.find(bulki.id)
92
+ bulk.ints.should == [1, 2]
93
+ bulk.strings.should == %w{as so}
94
+ bulk.floats.should == [1.0, 1.2]
95
+ bulk.decimals.should == [1.0, 1.2]
96
+ bulk.texts.should == [nil, 'Text', 'NULL', 'Text with nil', 'Text with , nil, !"\\', 'nil']
97
+ map_times(bulk.times).should ==
98
+ map_times(parse_times(%w{2010-01-01 2010-02-01}))
99
+ bulk.empty_def.should == []
100
+ bulk.destroy
101
+ end
102
+
103
+ it "should not alter defaults" do
104
+ bulk = Bulk.new
105
+ bulk.strings.push :foo
106
+ bulk = Bulk.new
107
+ bulk.strings.should == %w{as so}
108
+ end
109
+
110
+ it "should save changes" do
111
+ bulk = Bulk.find(3)
112
+ for field in %w{ints strings floats decimals times}
113
+ bulk.send(field+'=', bulk.send(field).reverse)
114
+ end
115
+ bulk.save!
116
+ bulk = Bulk.find(:first, :conditions=>'3 = id')
117
+ bulk.ints.should == [3, 2]
118
+ bulk.strings.should == %w{three two}
119
+ bulk.floats.should == [2.5, 2]
120
+ bulk.decimals.should == [2.5, 2]
121
+ map_times(bulk.times).should == map_times(parse_times(%w{2010-04-01 2010-03-01}))
122
+ end
123
+
124
+ it "should save right text" do
125
+ bulk = Bulk.find(5)
126
+ bulk.texts = ['Text with , nil, !\x01\\\'"',"Text with , nil, !\x01\n\\\'\""]
127
+ bulk.save!
128
+ bulk.texts = []
129
+ bulk = Bulk.find(:first, :conditions=>'5 = id')
130
+ bulk.texts.should == ['Text with , nil, !\x01\\\'"',"Text with , nil, !\x01\n\\\'\""]
131
+ end
132
+
133
+ it "should save nested arrays" do
134
+ Bulk.transaction do
135
+ bulk = Bulk.find(3)
136
+ bulk.ints = [[1,2],[3,4]]
137
+ bulk.floats = [[1.0, 2.3e34],[3.43,6.21]]
138
+ bulk.times = [parse_times(%w{2010-04-01 2011-04-01}), parse_times(%w{2011-05-01 2010-05-01})]
139
+ bulk.save!
140
+ bulk = Bulk.find(:first, :conditions=>'3 = id')
141
+ bulk.ints.should == [[1,2],[3,4]]
142
+ bulk.floats.should == [[1.0, 2.3e34],[3.43,6.21]]
143
+ bulk.times.map{|ts| map_times(ts)}.should == [
144
+ map_times(parse_times(%w{2010-04-01 2011-04-01})),
145
+ map_times(parse_times(%w{2011-05-01 2010-05-01}))]
146
+ raise ActiveRecord::Rollback
147
+ end
148
+ end
149
+
150
+ it "should save right nested text" do
151
+ Bulk.transaction do
152
+ bulk = Bulk.find(5)
153
+ bulk.texts = [['Text with , nil, !\x01\\\'"',"Text with , nil, !\x01\n\\\'\""], ['asdf', 'fdsa']]
154
+ bulk.save!
155
+ bulk.texts = []
156
+ bulk = Bulk.find(:first, :conditions=>'5 = id')
157
+ bulk.texts.should == [['Text with , nil, !\x01\\\'"',"Text with , nil, !\x01\n\\\'\""], ['asdf', 'fdsa']]
158
+ raise ActiveRecord::Rollback
159
+ end
160
+ end
161
+
162
+ it "should be safe for eval" do
163
+ bulk = Bulk.find(5)
164
+ bulk.strings.should == ["\#{1+1}", "\\\#{1+1}\"'\\z\x01", "\t\n"]
165
+ #$stderr.puts ActiveRecord::ConnectionAdapters::PostgreSQLColumn::ESCAPE_ARRAY.inspect
166
+ end
167
+
168
+ it "should allow to use sql" do
169
+ bulks_where(['ints && ?', [1,2].pg]).should == bulks_where(:id=>[1,2,3])
170
+ end
171
+
172
+ it "should allow to use finders" do
173
+ bulks_where(:ints => [2].search_any).should == bulks_where(:id=>[2,3])
174
+ bulks_where(:ints => [2,3].search_any).should == bulks_where(:id=>[2,3])
175
+ bulks_where(:ints => [1,2].search_any).should == bulks_where(:id=>[1,2,3])
176
+
177
+ bulks_where(:ints => [2].search_all).should == bulks_where(:id=>[2,3])
178
+ bulks_where(:ints => [2,3].search_all).should == bulks_where(:id=>[3])
179
+ bulks_where(:ints => [1,2].search_all).should == []
180
+
181
+ bulks_where(:ints => [2].search_subarray).should == bulks_where(:id=>[2,4])
182
+ bulks_where(:ints => [2,3].search_subarray).should == bulks_where(:id=>[2,3,4])
183
+ bulks_where(:ints => [1,2].search_subarray).should == bulks_where(:id=>[1,2,4])
184
+ end
185
+
186
+ it "should be cached in @attributes_cache" do
187
+ bulk = Bulk.find(1)
188
+ ar = bulk.ints
189
+ bulk.ints.should.equal?(ar)
190
+ ard = ar.dup
191
+ arn = (bulk.ints << 1)
192
+ arn.should equal(ar)
193
+ bulk.ints.should equal(ar)
194
+ bulk.ints.should == (ard + [1])
195
+ end
196
+
197
+ it 'should allways save changed value' do
198
+ bulk = Bulk.find(1)
199
+ old = bulk.ints.dup
200
+ bulk.ints << 2
201
+ new = bulk.ints.dup
202
+ bulk.save
203
+ bulk.reload
204
+ bulk.ints.should == new
205
+ end
206
+
207
+ it 'should not break yaml serialization on model with array' do
208
+ item = Item.find(1)
209
+ item.for_yaml = {:a => :b}
210
+ item.save.should be_true
211
+ copy = Item.find(1)
212
+ copy.for_yaml.should == {:a => :b}
213
+ copy.for_yaml = ['a', 'b']
214
+ copy.save.should be_true
215
+ copy = Item.find(1)
216
+ copy.for_yaml.should == ['a', 'b']
217
+ end
218
+
219
+ it 'should not break yaml serialization on unrelated model' do
220
+ item = Unrelated.find(1)
221
+ item.for_yaml.should == {'a' => 'b'}
222
+ item.for_yaml = {:a => :b}
223
+ item.save.should be_true
224
+ copy = Unrelated.find(1)
225
+ copy.for_yaml.should == {:a => :b}
226
+ copy.for_yaml = ['a', 'b']
227
+ copy.save.should be_true
228
+ copy = Unrelated.find(1)
229
+ copy.for_yaml.should == ['a', 'b']
230
+ end
231
+
232
+ it 'should not break custom serialization on model with array' do
233
+ obj = Unrelated::MySerializer.new(%w{hello world})
234
+ model = Unrelated.create!(:for_custom_serialize => obj)
235
+ model.for_custom_serialize.to_yaml.should == obj.to_yaml
236
+ model2 = Unrelated.find(model.id)
237
+ model2.for_custom_serialize.to_yaml.should == obj.to_yaml
238
+ end
239
+
240
+ it 'should be workable with sti' do
241
+ obj = Bulk1.where(:ints => [10].pg).first
242
+ obj.should be_instance_of Bulk1
243
+ obj.floats = [1.1, 2.2]
244
+ obj.save
245
+ obj1 = Bulk.find(obj.id)
246
+ obj1.should be_instance_of Bulk1
247
+ obj1.floats.should == [1.1, 2.2]
248
+ obj2 = Bulk1.new
249
+ obj2.save
250
+ obj2.destroy
251
+ end
252
+
253
+ def map_times(times)
254
+ times.map{|t| t.strftime("%F %T")}
255
+ end
256
+
257
+ def parse_times(times)
258
+ times.map{|t| DateTime.parse(t)}
259
+ end
260
+
261
+ def bulks_where(cond)
262
+ Bulk.where(cond).order('id').all
263
+ end
264
+ end
265
+
266
+ context "CanCan" do
267
+ before :all do
268
+ @ability_class = Class.new do
269
+ include CanCan::Ability
270
+ end
271
+ @all_items = Item.all
272
+ end
273
+
274
+ before :each do
275
+ @ability = @ability_class.new
276
+ end
277
+
278
+ it "should provide search_any for cancan" do
279
+ should_match_ids_with_ability [2, 3, 4, 6], :tag_ids => [3].search_any
280
+ should_match_ids_with_ability [1, 3, 4, 5, 6], :tag_ids => [1, 2].search_any
281
+ end
282
+
283
+ it "should provide search_all for cancan" do
284
+ should_match_ids_with_ability [2, 3, 4, 6], :tag_ids => [3].search_all
285
+ should_match_ids_with_ability [5, 6], :tag_ids => [1, 2].search_all
286
+ end
287
+
288
+ it "should provide search_subarray for cancan" do
289
+ should_match_ids_with_ability [2, 7], :tag_ids => [3].search_subarray
290
+ should_match_ids_with_ability [1, 5, 7], :tag_ids => [1, 2].search_subarray
291
+ end
292
+
293
+ def should_match_ids_with_ability(ids, ability)
294
+ act = (ability[:tag_ids].class.name + ids.join('_')).to_sym
295
+ ab.can act, Item, ability
296
+ items = accessible_items(act)
297
+ items.should == items_where(:id=>ids)
298
+ should_be_able_all items, act
299
+ should_not_be_able_except items, act
300
+ end
301
+
302
+ def ab
303
+ @ability
304
+ end
305
+
306
+ def accessible_items(act)
307
+ Item.accessible_by(ab, act).order('id').all
308
+ end
309
+
310
+ def items_where(cond)
311
+ Item.where(cond).order('id').all
312
+ end
313
+
314
+ def should_be_able_all(items, act)
315
+ items.each{|item| ab.should be_able_to(act, item)}
316
+ end
317
+
318
+ def should_not_be_able_except(items, act)
319
+ (@all_items - items).each{|item| ab.should_not be_able_to(act, items)}
320
+ end
321
+ end
322
+
323
+ context "references_by" do
324
+ it "should fetch tags in saved order" do
325
+ Item.find(3).tags.should == [Tag.find(1), Tag.find(3)]
326
+ Item.find(4).tags.should == [Tag.find(3), Tag.find(1)]
327
+ end
328
+
329
+ it "should save tags references" do
330
+ item = Item.find(3)
331
+ item.tags= [Tag.find(1), '3', 2]
332
+ item.tags.should == [Tag.find(1), Tag.find(3), Tag.find(2)]
333
+ item.save!
334
+ item.reload
335
+ item.tags.should == [Tag.find(1), Tag.find(3), Tag.find(2)]
336
+ item.tags= [1,3]
337
+ item.save!
338
+ item.reload
339
+ item.tags.should == [Tag.find(1), Tag.find(3)]
340
+ end
341
+
342
+ it "should define named scopes for tags" do
343
+ Item.tags_include(3).order('id').all.should == items_where(:id=>[2,3,4,6])
344
+ Item.tags_include(1,3).order('id').all.should == items_where(:id=>[3,4,6])
345
+ Item.tags_have_all(3).order('id').all.should == items_where(:id=>[2,3,4,6])
346
+ Item.tags_have_all(1,3).order('id').all.should == items_where(:id=>[3,4,6])
347
+ Item.tags_have_any(3).order('id').all.should == items_where(:id=>[2,3,4,6])
348
+ Item.tags_have_any(1,3).order('id').all.should == items_where(:id=>[1,2,3,4,5,6])
349
+ Item.tags_included_into(3).order('id').all.should == items_where(:id=>[2,7])
350
+ Item.tags_included_into(1,3).order('id').all.should == items_where(:id=>[1,2,3,4,7])
351
+ end
352
+
353
+ def items_where(cond)
354
+ Item.where(cond).order('id').all
355
+ end
356
+ end
357
+
358
+ context "schema" do
359
+ it "should allow to add column" do
360
+ lambda do
361
+ ActiveRecord::Schema.define do
362
+ change_table :items do |t|
363
+ t.integer_array :ints, :default=>[], :null=>false
364
+ end
365
+ add_column :items, :floats, :float_array, :default=>[0], :null=>false
366
+ end
367
+ end.should_not raise_error
368
+ end
369
+
370
+ it "should not break other add_column" do
371
+ lambda do
372
+ ActiveRecord::Schema.define do
373
+ add_column :items, :float1, :float
374
+ add_column :items, :float2, :float, :null=>true
375
+ add_column :items, :float3, :float, :default=>0, :null=>false
376
+ end
377
+ end.should_not raise_error
378
+ end
379
+ end
380
+ end