ar_pg_array 0.8.4 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/ar_pg_array/querying.rb +18 -8
- data/lib/ar_pg_array/querying_arel.rb +8 -2
- data/lib/ar_pg_array/references_by.rb +147 -63
- data/lib/ar_pg_array/schema.rb +3 -3
- data/spec/fixtures/bulk.rb +2 -0
- data/spec/fixtures/bulks.yml +28 -0
- data/spec/fixtures/item.rb +3 -0
- data/spec/fixtures/items.yml +22 -0
- data/spec/fixtures/schema.rb +21 -0
- data/spec/fixtures/tag.rb +2 -0
- data/spec/fixtures/tags.yml +15 -0
- data/spec/pg_array_spec.rb +221 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +49 -0
- metadata +25 -6
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/lib/ar_pg_array/querying.rb
CHANGED
@@ -17,13 +17,23 @@ module ActiveRecord
|
|
17
17
|
alias_method_chain :attribute_condition, :postgresql_arrays
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
if instance_method(:quote_bound_value).arity == 1
|
21
|
+
def quote_bound_value_with_postgresql_arrays(value)
|
22
|
+
if ::PGArrays::PgArray === value
|
23
|
+
connection.quote_array_by_base_type(value, value.base_type)
|
24
|
+
else
|
25
|
+
quote_bound_value_without_postgresql_arrays(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
else
|
29
|
+
def quote_bound_value_with_postgresql_arrays(value, c = connection)
|
30
|
+
if ::PGArrays::PgArray === value
|
31
|
+
c.quote_array_by_base_type(value, value.base_type)
|
32
|
+
else
|
33
|
+
quote_bound_value_without_postgresql_arrays(value, c)
|
34
|
+
end
|
25
35
|
end
|
26
|
-
end
|
36
|
+
end
|
27
37
|
alias_method_chain :quote_bound_value, :postgresql_arrays
|
28
38
|
end
|
29
39
|
end
|
@@ -55,13 +65,13 @@ module PGArrays
|
|
55
65
|
end
|
56
66
|
|
57
67
|
class PgAll
|
58
|
-
def include?
|
68
|
+
def include?(v)
|
59
69
|
Array === v && (self - v).empty?
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
63
73
|
class PgIncludes < PgArray
|
64
|
-
def include?
|
74
|
+
def include?(v)
|
65
75
|
Array === v && (v - self).empty?
|
66
76
|
end
|
67
77
|
end
|
@@ -32,7 +32,7 @@ module Arel
|
|
32
32
|
end
|
33
33
|
|
34
34
|
class Attribute
|
35
|
-
|
35
|
+
methods = lambda do
|
36
36
|
def ar_any(other)
|
37
37
|
Predicates::ArrayAny.new(self, other)
|
38
38
|
end
|
@@ -58,13 +58,19 @@ module Arel
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
+
if defined? PREDICATES
|
62
|
+
PREDICATES.concat [:ar_any, :ar_all, :ar_included]
|
63
|
+
class_exec &methods
|
64
|
+
else
|
65
|
+
Predications.class_exec &methods
|
66
|
+
end
|
61
67
|
end
|
62
68
|
end
|
63
69
|
|
64
70
|
module PGArrays
|
65
71
|
class PgArray
|
66
72
|
def to_sql( formatter = nil )
|
67
|
-
formatter.engine.quote_array_for_arel_by_base_type(self, base_type)
|
73
|
+
formatter.engine.connection.quote_array_for_arel_by_base_type(self, base_type)
|
68
74
|
end
|
69
75
|
|
70
76
|
def to_a
|
@@ -1,69 +1,153 @@
|
|
1
1
|
module PGArrays
|
2
2
|
module ReferencesBy
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
when nil then nil
|
15
|
-
else v.to_i
|
16
|
-
end
|
17
|
-
}.compact
|
18
|
-
obj.write_attribute( field, value )
|
19
|
-
end
|
20
|
-
|
21
|
-
def validate(obj)
|
22
|
-
has_ids = klass.find(:all, :select=>'id', :conditions=>{:id=>obj.read_attribute(field)}).map(&:id)
|
23
|
-
unless has_ids.sort == obj.read_attribute(field).sort
|
24
|
-
obj.errors.add(relation, :wrong_array_reference)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def for_referenced(obj_klass)
|
29
|
-
condition = lambda do |obj|
|
30
|
-
{ :conditions=>{field.to_sym => case obj when klass then obj.id else obj.to_i end} }
|
31
|
-
end
|
32
|
-
obj_klass.send(:named_scope, "#{relation}_include", condition )
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
module ClassMethods
|
38
|
-
def references_by_array( relation, options = {} )
|
39
|
-
unless ActiveSupport::Memoizable === self
|
40
|
-
extend ActiveSupport::Memoizable
|
41
|
-
end
|
3
|
+
if defined? ::Arel
|
4
|
+
HAS_AREL=true
|
5
|
+
NAMED_SCOPE='scope'
|
6
|
+
else
|
7
|
+
NAMED_SCOPE='named_scope'
|
8
|
+
if defined? ::FakeArel
|
9
|
+
HAS_AREL= true
|
10
|
+
else
|
11
|
+
HAS_AREL=false
|
12
|
+
end
|
13
|
+
end
|
42
14
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
15
|
+
class RelationHolder < Struct.new(:relation, :field, :klass)
|
16
|
+
|
17
|
+
def referenced(obj)
|
18
|
+
ids = obj[field] || []
|
19
|
+
objs = klass.find_all_by_id( ids.sort )
|
20
|
+
if ids.size < 20
|
21
|
+
objs.sort_by{|o| ids.index(o.id)}
|
22
|
+
else
|
23
|
+
to_ind = ids.each_with_index.inject({}){|h, (v,i)| h[v]=i; h}
|
24
|
+
objs.sort_by{|o| to_ind[o.id]}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_referenced(obj, value)
|
29
|
+
obj[field] = map_to_ids(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
if HAS_AREL
|
33
|
+
def validate(obj)
|
34
|
+
has_ids = klass.where(:id=>obj.read_attribute(field)).
|
35
|
+
select('id').all.map(&:id)
|
36
|
+
unless has_ids.sort == obj.read_attribute(field).sort
|
37
|
+
obj.errors.add(relation, :wrong_array_reference)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def for_referenced(obj_klass)
|
42
|
+
myself = self
|
43
|
+
obj_klass.class_exec do
|
44
|
+
puts "named_scope #{NAMED_SCOPE} #{myself.relation}_include"
|
45
|
+
self.send NAMED_SCOPE, "#{myself.relation}_include", lambda{|*objs|
|
46
|
+
objs = myself.map_to_ids objs.flatten
|
47
|
+
where(myself.field.to_sym => objs.search_all)
|
48
|
+
}
|
49
|
+
|
50
|
+
self.send NAMED_SCOPE, "#{myself.relation}_have_all", lambda{|*objs|
|
51
|
+
objs = myself.map_to_ids objs.flatten
|
52
|
+
where(myself.field.to_sym => objs.search_all)
|
53
|
+
}
|
54
|
+
|
55
|
+
self.send NAMED_SCOPE, "#{myself.relation}_have_any", lambda{|*objs|
|
56
|
+
objs = myself.map_to_ids objs.flatten
|
57
|
+
where(myself.field.to_sym => objs.search_any)
|
58
|
+
}
|
59
|
+
|
60
|
+
self.send NAMED_SCOPE, "#{myself.relation}_included_into", lambda{|*objs|
|
61
|
+
objs = myself.map_to_ids objs.flatten
|
62
|
+
where(myself.field.to_sym => objs.search_subarray)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
def validate(obj)
|
68
|
+
has_ids = klass.find(:all,
|
69
|
+
:select=>'id',
|
70
|
+
:conditions=>{:id=>obj.read_attribute(field)}
|
71
|
+
).map(&:id)
|
72
|
+
unless has_ids.sort == obj.read_attribute(field).sort
|
73
|
+
obj.errors.add(relation, :wrong_array_reference)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def for_referenced(obj_klass)
|
78
|
+
myself = self
|
79
|
+
obj_klass.class_exec do
|
80
|
+
named_scope "#{myself.relation}_include", lambda{|*objs|
|
81
|
+
objs = myself.map_to_ids objs.flatten
|
82
|
+
{ :conditions=>{ myself.field.to_sym => objs.search_all } }
|
83
|
+
}
|
84
|
+
|
85
|
+
named_scope "#{myself.relation}_have_all", lambda{|*objs|
|
86
|
+
objs = myself.map_to_ids objs.flatten
|
87
|
+
{ :conditions=>{ myself.field.to_sym => objs.search_all } }
|
88
|
+
}
|
89
|
+
|
90
|
+
named_scope "#{myself.relation}_have_any", lambda{|*objs|
|
91
|
+
objs = myself.map_to_ids objs.flatten
|
92
|
+
{ :conditions=>{ myself.field.to_sym => objs.search_any } }
|
93
|
+
}
|
94
|
+
|
95
|
+
named_scope "#{myself.relation}_included_into", lambda{|*objs|
|
96
|
+
objs = myself.map_to_ids objs.flatten
|
97
|
+
{ :conditions=>{ myself.field.to_sym => objs.search_subarray } }
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def val_to_id(val)
|
104
|
+
case val
|
105
|
+
when ActiveRecord::Base then val.id
|
106
|
+
when nil then nil
|
107
|
+
else val.to_i
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def map_to_ids(vals)
|
112
|
+
(r = vals.map{|v| val_to_id(v)}).compact!
|
113
|
+
r
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module ClassMethods
|
118
|
+
def references_by_array( relation, options = {} )
|
119
|
+
unless ActiveSupport::Memoizable === self
|
120
|
+
extend ActiveSupport::Memoizable
|
121
|
+
end
|
122
|
+
|
123
|
+
relation = relation.to_s.pluralize
|
124
|
+
field = "#{relation.singularize}_ids"
|
125
|
+
klass_name = (options[:class_name] || relation).to_s.singularize.camelize
|
126
|
+
klass = klass_name.constantize
|
127
|
+
holder = RelationHolder.new(relation, field, klass )
|
128
|
+
|
129
|
+
meths = Module.new do
|
130
|
+
define_method(relation) do
|
131
|
+
holder.referenced(self)
|
132
|
+
end
|
133
|
+
|
134
|
+
define_method("#{relation}=") do |value|
|
135
|
+
flush_cache(relation)
|
136
|
+
holder.set_referenced(self, value)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
include meths
|
140
|
+
|
141
|
+
memoize relation
|
142
|
+
|
143
|
+
if options[:validate]
|
144
|
+
validate {|o| holder.validate(o)}
|
145
|
+
end
|
146
|
+
|
147
|
+
holder.for_referenced(self)
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
67
151
|
end
|
68
152
|
end
|
69
153
|
|
data/lib/ar_pg_array/schema.rb
CHANGED
@@ -216,7 +216,7 @@ module ActiveRecord
|
|
216
216
|
options = args.extract_options! # options = args.extract_options!
|
217
217
|
column_names = args # column_names = args
|
218
218
|
#
|
219
|
-
column_names.each { |name| column(name, '#{key}_array', options) }# column_names.each { |name| column(name, 'string_array', options) }
|
219
|
+
column_names.each { |name| column(name, :'#{key}_array', options) }# column_names.each { |name| column(name, 'string_array', options) }
|
220
220
|
end # end
|
221
221
|
EOV
|
222
222
|
Table.class_eval <<-EOV
|
@@ -225,7 +225,7 @@ module ActiveRecord
|
|
225
225
|
column_names = args # column_names = args
|
226
226
|
#
|
227
227
|
column_names.each { |name| # column_names.each { |name|
|
228
|
-
@base.add_column(@table_name, name, '#{key}_array', options) # @base.add_column(@table_name, name, 'string_array', options) }
|
228
|
+
@base.add_column(@table_name, name, :'#{key}_array', options) # @base.add_column(@table_name, name, 'string_array', options) }
|
229
229
|
} # }
|
230
230
|
end # end
|
231
231
|
EOV
|
@@ -234,7 +234,7 @@ module ActiveRecord
|
|
234
234
|
|
235
235
|
def type_to_sql_with_postgresql_arrays(type, limit = nil, precision = nil, scale = nil)
|
236
236
|
if type.to_s =~ /^(.+)_array$/
|
237
|
-
type_to_sql_without_postgresql_arrays($1, limit, precision, scale)+'[]'
|
237
|
+
type_to_sql_without_postgresql_arrays($1.to_sym, limit, precision, scale)+'[]'
|
238
238
|
else
|
239
239
|
type_to_sql_without_postgresql_arrays(type, limit, precision, scale)
|
240
240
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
first:
|
2
|
+
id: 1
|
3
|
+
ints: [ 1 ]
|
4
|
+
strings: [ one ]
|
5
|
+
times: [<%= Time.now.at_beginning_of_month.to_s(:db)%>, <%= Time.now.at_beginning_of_day.to_s(:db) %>]
|
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
|
+
four:
|
23
|
+
id: 4
|
24
|
+
ints: [ ]
|
25
|
+
strings: [ ]
|
26
|
+
times: [ ]
|
27
|
+
floats: [ ]
|
28
|
+
decimals: [ ]
|
@@ -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,21 @@
|
|
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
|
+
end
|
12
|
+
|
13
|
+
create_table "bulks", :force => true do |t|
|
14
|
+
t.string :value
|
15
|
+
t.integer_array :ints, :default => [1, 2]
|
16
|
+
t.string_array :strings, :default => %w{as so}
|
17
|
+
t.timestamp_array :times, :default => %w{2010-01-01 2010-02-01}
|
18
|
+
t.float_array :floats, :default => [1.0, 1.2]
|
19
|
+
t.decimal_array :decimals, :default => [1.0, 1.2]
|
20
|
+
end
|
21
|
+
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,221 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe "PgArray" do
|
4
|
+
describe "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
|
+
describe "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
|
+
bulk.times.should == [Time.now.at_beginning_of_month, Time.now.at_beginning_of_day]
|
71
|
+
bulk.floats.should == [1.0, 2.3]
|
72
|
+
bulk.decimals.should == [1.0, 2.3]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should be created with defaults" do
|
76
|
+
bulk = Bulk.new
|
77
|
+
bulk.ints.should == [1, 2]
|
78
|
+
bulk.strings.should == %w{as so}
|
79
|
+
bulk.floats.should == [1.0, 1.2]
|
80
|
+
bulk.decimals.should == [1.0, 1.2]
|
81
|
+
map_times(bulk.times).should ==
|
82
|
+
map_times(parse_times(%w{2010-01-01 2010-02-01}))
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should save changes" do
|
86
|
+
bulk = Bulk.find(3)
|
87
|
+
for field in %w{ints strings floats decimals times}
|
88
|
+
bulk.send(field+'=', bulk.send(field).reverse)
|
89
|
+
end
|
90
|
+
bulk.save!
|
91
|
+
bulk = Bulk.find(:first, :conditions=>'3 = id')
|
92
|
+
bulk.ints.should == [3, 2]
|
93
|
+
bulk.strings.should == %w{three two}
|
94
|
+
bulk.floats.should == [2.5, 2]
|
95
|
+
bulk.decimals.should == [2.5, 2]
|
96
|
+
map_times(bulk.times).should == map_times(parse_times(%w{2010-04-01 2010-03-01}))
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should allow to use sql" do
|
100
|
+
bulks_where(['ints && ?', [1,2].pg]).should == bulks_where(:id=>[1,2,3])
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should allow to use finders" do
|
104
|
+
bulks_where(:ints => [2].search_any).should == bulks_where(:id=>[2,3])
|
105
|
+
bulks_where(:ints => [2,3].search_any).should == bulks_where(:id=>[2,3])
|
106
|
+
bulks_where(:ints => [1,2].search_any).should == bulks_where(:id=>[1,2,3])
|
107
|
+
|
108
|
+
bulks_where(:ints => [2].search_all).should == bulks_where(:id=>[2,3])
|
109
|
+
bulks_where(:ints => [2,3].search_all).should == bulks_where(:id=>[3])
|
110
|
+
bulks_where(:ints => [1,2].search_all).should == []
|
111
|
+
|
112
|
+
bulks_where(:ints => [2].search_subarray).should == bulks_where(:id=>[2,4])
|
113
|
+
bulks_where(:ints => [2,3].search_subarray).should == bulks_where(:id=>[2,3,4])
|
114
|
+
bulks_where(:ints => [1,2].search_subarray).should == bulks_where(:id=>[1,2,4])
|
115
|
+
end
|
116
|
+
|
117
|
+
def map_times(times)
|
118
|
+
times.map{|t| t.strftime("%F %T")}
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_times(times)
|
122
|
+
times.map{|t| DateTime.parse(t)}
|
123
|
+
end
|
124
|
+
|
125
|
+
def bulks_where(cond)
|
126
|
+
Bulk.where(cond).order('id').all
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "CanCan" do
|
131
|
+
before :all do
|
132
|
+
@ability_class = Class.new do
|
133
|
+
include CanCan::Ability
|
134
|
+
end
|
135
|
+
@all_items = Item.all
|
136
|
+
end
|
137
|
+
|
138
|
+
before :each do
|
139
|
+
@ability = @ability_class.new
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should provide search_any for cancan" do
|
143
|
+
should_match_ids_with_ability [2, 3, 4, 6], :tag_ids => [3].search_any
|
144
|
+
should_match_ids_with_ability [1, 3, 4, 5, 6], :tag_ids => [1, 2].search_any
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should provide search_all for cancan" do
|
148
|
+
should_match_ids_with_ability [2, 3, 4, 6], :tag_ids => [3].search_all
|
149
|
+
should_match_ids_with_ability [5, 6], :tag_ids => [1, 2].search_all
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should provide search_subarray for cancan" do
|
153
|
+
should_match_ids_with_ability [2, 7], :tag_ids => [3].search_subarray
|
154
|
+
should_match_ids_with_ability [1, 5, 7], :tag_ids => [1, 2].search_subarray
|
155
|
+
end
|
156
|
+
|
157
|
+
def should_match_ids_with_ability(ids, ability)
|
158
|
+
act = (ability[:tag_ids].class.name + ids.join('_')).to_sym
|
159
|
+
ab.can act, Item, ability
|
160
|
+
items = accessible_items(act)
|
161
|
+
items.should == items_where(:id=>ids)
|
162
|
+
should_be_able_all items, act
|
163
|
+
should_not_be_able_except items, act
|
164
|
+
end
|
165
|
+
|
166
|
+
def ab
|
167
|
+
@ability
|
168
|
+
end
|
169
|
+
|
170
|
+
def accessible_items(act)
|
171
|
+
Item.accessible_by(ab, act).order('id').all
|
172
|
+
end
|
173
|
+
|
174
|
+
def items_where(cond)
|
175
|
+
Item.where(cond).order('id').all
|
176
|
+
end
|
177
|
+
|
178
|
+
def should_be_able_all(items, act)
|
179
|
+
items.each{|item| ab.should be_able_to(act, item)}
|
180
|
+
end
|
181
|
+
|
182
|
+
def should_not_be_able_except(items, act)
|
183
|
+
(@all_items - items).each{|item| ab.should_not be_able_to(act, items)}
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "references_by" do
|
188
|
+
it "should fetch tags in saved order" do
|
189
|
+
Item.find(3).tags.should == [Tag.find(1), Tag.find(3)]
|
190
|
+
Item.find(4).tags.should == [Tag.find(3), Tag.find(1)]
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should save tags references" do
|
194
|
+
item = Item.find(3)
|
195
|
+
item.tags= [Tag.find(1), '3', 2]
|
196
|
+
item.tags.should == [Tag.find(1), Tag.find(3), Tag.find(2)]
|
197
|
+
item.save!
|
198
|
+
item.reload
|
199
|
+
item.tags.should == [Tag.find(1), Tag.find(3), Tag.find(2)]
|
200
|
+
item.tags= [1,3]
|
201
|
+
item.save!
|
202
|
+
item.reload
|
203
|
+
item.tags.should == [Tag.find(1), Tag.find(3)]
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should define named scopes for tags" do
|
207
|
+
Item.tags_include(3).order('id').all.should == items_where(:id=>[2,3,4,6])
|
208
|
+
Item.tags_include(1,3).order('id').all.should == items_where(:id=>[3,4,6])
|
209
|
+
Item.tags_have_all(3).order('id').all.should == items_where(:id=>[2,3,4,6])
|
210
|
+
Item.tags_have_all(1,3).order('id').all.should == items_where(:id=>[3,4,6])
|
211
|
+
Item.tags_have_any(3).order('id').all.should == items_where(:id=>[2,3,4,6])
|
212
|
+
Item.tags_have_any(1,3).order('id').all.should == items_where(:id=>[1,2,3,4,5,6])
|
213
|
+
Item.tags_included_into(3).order('id').all.should == items_where(:id=>[2,7])
|
214
|
+
Item.tags_included_into(1,3).order('id').all.should == items_where(:id=>[1,2,3,4,7])
|
215
|
+
end
|
216
|
+
|
217
|
+
def items_where(cond)
|
218
|
+
Item.where(cond).order('id').all
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec'
|
3
|
+
gem 'activerecord'
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
6
|
+
$:.unshift(File.dirname(__FILE__) + '/fixtures')
|
7
|
+
|
8
|
+
require 'active_record'
|
9
|
+
require 'active_record/fixtures'
|
10
|
+
gem 'pg'
|
11
|
+
begin
|
12
|
+
gem 'arel'
|
13
|
+
rescue Gem::LoadError
|
14
|
+
require 'fake_arel'
|
15
|
+
class ActiveRecord::Base
|
16
|
+
named_scope :joins, lambda {|*join| {:joins => join } if join[0]}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
require 'cancan'
|
20
|
+
require 'cancan/matchers'
|
21
|
+
require 'ar_pg_array'
|
22
|
+
|
23
|
+
|
24
|
+
ActiveRecord::Base.establish_connection(
|
25
|
+
:adapter => 'postgresql',
|
26
|
+
:database => 'postgres',
|
27
|
+
:encoding => 'utf8'
|
28
|
+
)
|
29
|
+
ActiveRecord::Base.connection.create_database('test_pg_array', :encoding=>'utf8') rescue nil
|
30
|
+
|
31
|
+
ActiveRecord::Base.establish_connection(
|
32
|
+
:adapter => 'postgresql',
|
33
|
+
:database => 'test_pg_array',
|
34
|
+
:encoding => 'utf8'
|
35
|
+
)
|
36
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT) #if $0 == 'irb'
|
37
|
+
|
38
|
+
require 'tag'
|
39
|
+
require 'item'
|
40
|
+
require 'bulk'
|
41
|
+
ActiveRecord::Base.silence do
|
42
|
+
ActiveRecord::Migration.verbose = false
|
43
|
+
|
44
|
+
# load schema
|
45
|
+
load File.join('spec/fixtures/schema.rb')
|
46
|
+
# load fixtures
|
47
|
+
Fixtures.create_fixtures("spec/fixtures", ActiveRecord::Base.connection.tables)
|
48
|
+
end
|
49
|
+
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_pg_array
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Sokolov Yura aka funny_falcon
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-09-04 00:00:00 +04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
28
30
|
segments:
|
29
31
|
- 0
|
30
32
|
version: "0"
|
@@ -50,6 +52,16 @@ files:
|
|
50
52
|
- lib/ar_pg_array/references_by.rb
|
51
53
|
- lib/ar_pg_array/schema.rb
|
52
54
|
- lib/ar_pg_array/schema_arel.rb
|
55
|
+
- spec/fixtures/bulk.rb
|
56
|
+
- spec/fixtures/bulks.yml
|
57
|
+
- spec/fixtures/item.rb
|
58
|
+
- spec/fixtures/items.yml
|
59
|
+
- spec/fixtures/schema.rb
|
60
|
+
- spec/fixtures/tag.rb
|
61
|
+
- spec/fixtures/tags.yml
|
62
|
+
- spec/pg_array_spec.rb
|
63
|
+
- spec/spec.opts
|
64
|
+
- spec/spec_helper.rb
|
53
65
|
has_rdoc: true
|
54
66
|
homepage: http://github.com/funny-falcon/activerecord-postgresql-arrays
|
55
67
|
licenses: []
|
@@ -64,6 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
76
|
requirements:
|
65
77
|
- - ">="
|
66
78
|
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
67
80
|
segments:
|
68
81
|
- 0
|
69
82
|
version: "0"
|
@@ -72,6 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
85
|
requirements:
|
73
86
|
- - ">="
|
74
87
|
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
75
89
|
segments:
|
76
90
|
- 0
|
77
91
|
version: "0"
|
@@ -82,5 +96,10 @@ rubygems_version: 1.3.7
|
|
82
96
|
signing_key:
|
83
97
|
specification_version: 3
|
84
98
|
summary: Use power of PostgreSQL Arrays in ActiveRecord
|
85
|
-
test_files:
|
86
|
-
|
99
|
+
test_files:
|
100
|
+
- spec/fixtures/schema.rb
|
101
|
+
- spec/fixtures/item.rb
|
102
|
+
- spec/fixtures/bulk.rb
|
103
|
+
- spec/fixtures/tag.rb
|
104
|
+
- spec/pg_array_spec.rb
|
105
|
+
- spec/spec_helper.rb
|