ar_pg_array 0.8.4 → 0.9.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.
- 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
|