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.
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +48 -0
- data/Rakefile +7 -0
- data/ar_jdbc_pg_array.gemspec +24 -0
- data/lib/ar_jdbc_pg_array.rb +12 -0
- data/lib/ar_jdbc_pg_array/allways_save.rb +38 -0
- data/lib/ar_jdbc_pg_array/parser.rb +174 -0
- data/lib/ar_jdbc_pg_array/querying.rb +72 -0
- data/lib/ar_jdbc_pg_array/querying_arel.rb +172 -0
- data/lib/ar_jdbc_pg_array/references_by.rb +154 -0
- data/lib/ar_jdbc_pg_array/schema.rb +248 -0
- data/lib/ar_jdbc_pg_array/schema_arel.rb +47 -0
- data/lib/ar_jdbc_pg_array/schema_cacheable.rb +6 -0
- data/lib/ar_jdbc_pg_array/schema_fix_will_change.rb +13 -0
- data/spec/fixtures/bulk.rb +5 -0
- data/spec/fixtures/bulks.yml +36 -0
- data/spec/fixtures/item.rb +4 -0
- data/spec/fixtures/items.yml +22 -0
- data/spec/fixtures/schema.rb +30 -0
- data/spec/fixtures/tag.rb +2 -0
- data/spec/fixtures/tags.yml +15 -0
- data/spec/fixtures/unrelated.rb +21 -0
- data/spec/fixtures/unrelateds.yml +3 -0
- data/spec/pg_array_spec.rb +380 -0
- data/spec/spec_helper.rb +55 -0
- metadata +186 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Sokolov Yura aka funny_falcon
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PostgresArrays
|
2
|
+
==============
|
3
|
+
|
4
|
+
This library adds ability to use PostgreSQL array types with ActiveRecord.
|
5
|
+
|
6
|
+
> User.find(:all, :conditions=>['arr @> ?', [1,2,3].pg])
|
7
|
+
SELECT * FROM "users" WHERE ('arr' @> E'{"1", "2", "3"}')
|
8
|
+
> User.find(:all, :conditions=>['arr @> ?', [1,2,3].pg(:integer)])
|
9
|
+
SELECT * FROM "users" WHERE (arr @> '{1,2,3}')
|
10
|
+
> User.find(:all, :conditions=>['arr @> ?', [1,2,3].pg(:float)])
|
11
|
+
SELECT * FROM "users" WHERE (arr @> '{1.0,2.0,3.0}')
|
12
|
+
> u = User.find(1)
|
13
|
+
SELECT * FROM "users" WHERE ("users"."id" = 1)
|
14
|
+
=> #<User id: 1, ..., arr: [1,2]>
|
15
|
+
> u.arr = [3,4]
|
16
|
+
> u.save
|
17
|
+
UPDATE "users" SET "db_ar" = '{3.0,4.0}' WHERE "id" = 19
|
18
|
+
> User.find(:all, :conditions=>{:arr=>[3,4].pg})
|
19
|
+
SELECT * FROM "users" WHERE ("users"."arr" = E'{"3", "4"}')
|
20
|
+
> User.find(:all, :conditions=>{:arr=>[3,4].search_any(:float)})
|
21
|
+
SELECT * FROM "users" WHERE ("users"."arr" && '{3.0,4.0}')
|
22
|
+
> User.find(:all, :conditions=>{:arr=>[3,4].search_all(:integer)})
|
23
|
+
SELECT * FROM "users" WHERE ("users"."arr" @> '{3,4}')
|
24
|
+
> User.find(:all, :conditions=>{:arr=>[3,4].search_subarray(:safe)})
|
25
|
+
SELECT * FROM "users" WHERE ("users"."arr" <@ '{3,4}')
|
26
|
+
|
27
|
+
class U < ActiveRecord::Migration
|
28
|
+
def self.up
|
29
|
+
create_table :users do |t|
|
30
|
+
t.integer_array :int_ar
|
31
|
+
end
|
32
|
+
add_column :users, :fl_ar, :float_array
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Installation
|
37
|
+
============
|
38
|
+
|
39
|
+
gem install ar_jdbc_pg_array
|
40
|
+
|
41
|
+
Changelog
|
42
|
+
=========
|
43
|
+
|
44
|
+
0.1.0
|
45
|
+
|
46
|
+
Initial jdbc support
|
47
|
+
|
48
|
+
Copyright (c) 2010 Sokolov Yura aka funny_falcon, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
Gem::Specification.new do |gem|
|
3
|
+
gem.name = 'ar_jdbc_pg_array'
|
4
|
+
gem.version = '0.1.0'
|
5
|
+
gem.platform = 'java'
|
6
|
+
|
7
|
+
gem.authors = ['Sokolov Yura', 'Dimko']
|
8
|
+
gem.email = ['deemox@gmail.com']
|
9
|
+
gem.description = "ar_jdbc_pg_array includes support of PostgreSQL's int[], float[], text[], timestamptz[] etc. into ActiveRecord. You could define migrations for array columns, query on array columns."
|
10
|
+
gem.summary = 'Use power of PostgreSQL Arrays in ActiveRecord'
|
11
|
+
gem.homepage = 'https://github.com/dimko/activerecord-jdbc-postgresql-arrays'
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($/)
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
|
17
|
+
gem.add_dependency 'activerecord', '~> 3.1'
|
18
|
+
gem.add_dependency 'activerecord-jdbcpostgresql-adapter', '~> 1.2.9'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'cancan'
|
22
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
23
|
+
gem.add_development_dependency 'rspec', '~> 2.5'
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_record/base'
|
3
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
4
|
+
require 'arjdbc/postgresql'
|
5
|
+
|
6
|
+
require 'ar_jdbc_pg_array/schema'
|
7
|
+
require 'ar_jdbc_pg_array/schema_cacheable'
|
8
|
+
require 'ar_jdbc_pg_array/querying'
|
9
|
+
require 'ar_jdbc_pg_array/allways_save'
|
10
|
+
require 'ar_jdbc_pg_array/references_by'
|
11
|
+
require 'ar_jdbc_pg_array/schema_arel'
|
12
|
+
require 'ar_jdbc_pg_array/querying_arel'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module CheckArrayBeforeUpdate
|
3
|
+
def mark_arrays_for_update
|
4
|
+
@attributes_cache.each do |name, value|
|
5
|
+
attribute_will_change!(name) if Array === value && _read_attribute(name) != value
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
if VERSION::MAJOR < 3
|
10
|
+
module CheckArrayBeforeUpdate
|
11
|
+
def self.included(base)
|
12
|
+
base.alias_method_chain :update, :check_array
|
13
|
+
base.send(:alias_method, :_read_attribute, :read_attribute)
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_with_check_array
|
17
|
+
mark_arrays_for_update
|
18
|
+
update_without_check_array
|
19
|
+
end
|
20
|
+
end
|
21
|
+
else
|
22
|
+
module CheckArrayBeforeUpdate
|
23
|
+
include ActiveSupport::Concern
|
24
|
+
|
25
|
+
if VERSION::MAJOR == 3 && VERSION::MINOR >= 2
|
26
|
+
def _read_attribute(attr_name)
|
27
|
+
column_for_attribute(attr_name).type_cast(@attributes[attr_name])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def update(*)
|
32
|
+
mark_arrays_for_update
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Base.__send__ :include, CheckArrayBeforeUpdate
|
38
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PgArrayParser
|
4
|
+
CURLY_BRACKETS = '{}'.freeze
|
5
|
+
SQUARE_BRACKETS = '[]'.freeze
|
6
|
+
NULL = 'NULL'.freeze
|
7
|
+
NIL = 'nil'.freeze
|
8
|
+
ESCAPE_HASH={'\\'.freeze=>'\\\\'.freeze, '"'.freeze=>'\\"'.freeze}
|
9
|
+
|
10
|
+
def parse_numeric_pgarray(text)
|
11
|
+
text = text.tr(CURLY_BRACKETS, SQUARE_BRACKETS)
|
12
|
+
text.downcase!
|
13
|
+
JSON.load(text)
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_safe_pgarray(text, &block)
|
17
|
+
if text =~ /^\{([^\}]*)\}$/
|
18
|
+
$1.split(/,\s*/).map!{|v| v == NULL ? nil : yield(v)}
|
19
|
+
else
|
20
|
+
raise "Mailformed array" unless text =~ /^\{\s*/
|
21
|
+
ar, rest = _parse_safe_pgarray($')
|
22
|
+
ar
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def _parse_safe_pgarray(text, &block)
|
27
|
+
values = []
|
28
|
+
return values if text =~ /^\}\s*/
|
29
|
+
if text =~ /^\{\s*/
|
30
|
+
text = $'
|
31
|
+
while true
|
32
|
+
ar, rest = _parse_safe_pgarray(text, &block)
|
33
|
+
values << ar
|
34
|
+
if rest =~ /^\}\s*/
|
35
|
+
return values, $'
|
36
|
+
elsif rest =~ /^,\s*/
|
37
|
+
rest = $'
|
38
|
+
else
|
39
|
+
raise "Mailformed postgres array"
|
40
|
+
end
|
41
|
+
text = rest
|
42
|
+
end
|
43
|
+
else
|
44
|
+
while true
|
45
|
+
raise 'Mailformed Array' unless text =~ /^([^,\}]*)([,\}])\s*/
|
46
|
+
val, sep, rest = $1, $2, $'
|
47
|
+
values << (val == NULL ? nil : yield(val))
|
48
|
+
if sep == '}'
|
49
|
+
return values, rest
|
50
|
+
end
|
51
|
+
text = rest
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_pgarray(text, &block)
|
57
|
+
raise "Mailformed postgres array" unless text =~ /^\{\s*/
|
58
|
+
ar, rest = _parse_pgarray($', &block)
|
59
|
+
ar
|
60
|
+
end
|
61
|
+
|
62
|
+
def _parse_pgarray(text, &block)
|
63
|
+
values = []
|
64
|
+
return values if text =~ /^\}\s*/
|
65
|
+
if text =~ /^\{\s*/
|
66
|
+
text = $'
|
67
|
+
while true
|
68
|
+
ar, rest = _parse_pgarray(text, &block)
|
69
|
+
values << ar
|
70
|
+
if rest =~ /^\}\s*/
|
71
|
+
return values, $'
|
72
|
+
elsif rest =~ /^,\s*\{\s*/
|
73
|
+
rest = $'
|
74
|
+
else
|
75
|
+
raise "Mailformed postgres array"
|
76
|
+
end
|
77
|
+
text = rest
|
78
|
+
end
|
79
|
+
else
|
80
|
+
while true
|
81
|
+
if text =~ /^"((?:\\.|[^"\\])*)"([,}])\s*/
|
82
|
+
val, sep, rest = $1, $2, $'
|
83
|
+
val.gsub!(/\\(.)/, '\1')
|
84
|
+
val = yield val
|
85
|
+
elsif text =~ /^([^,\}]*)([,}])\s*/
|
86
|
+
val, sep, rest = $1, $2, $'
|
87
|
+
val = val == NULL ? nil : yield(val)
|
88
|
+
else
|
89
|
+
raise "Mailformed postgres array"
|
90
|
+
end
|
91
|
+
values << val
|
92
|
+
if sep == '}'
|
93
|
+
return values, rest
|
94
|
+
end
|
95
|
+
text = rest
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def _remap_array(array, &block)
|
101
|
+
array.map{|v|
|
102
|
+
case v
|
103
|
+
when Array
|
104
|
+
_remap_array(v, &block)
|
105
|
+
when nil
|
106
|
+
nil
|
107
|
+
else
|
108
|
+
yield v
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def prepare_pg_integer_array(value)
|
114
|
+
val = _remap_array(value){|v| v.to_i}.inspect
|
115
|
+
val.gsub!(NIL, NULL)
|
116
|
+
val.tr!(SQUARE_BRACKETS, CURLY_BRACKETS)
|
117
|
+
val
|
118
|
+
end
|
119
|
+
|
120
|
+
def prepare_pg_float_array(value)
|
121
|
+
val = _remap_array(value){|v| v.to_f}.inspect
|
122
|
+
val.gsub!(NIL, NULL)
|
123
|
+
val.tr!(SQUARE_BRACKETS, CURLY_BRACKETS)
|
124
|
+
val
|
125
|
+
end
|
126
|
+
|
127
|
+
def prepare_pg_safe_array(value)
|
128
|
+
value = value.map{|val|
|
129
|
+
case val
|
130
|
+
when Array
|
131
|
+
prepare_pg_safe_array(val)
|
132
|
+
when nil
|
133
|
+
NULL
|
134
|
+
else
|
135
|
+
val.to_s
|
136
|
+
end
|
137
|
+
}.join(',')
|
138
|
+
"{#{value}}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def prepare_pg_text_array(value)
|
142
|
+
value = value.map{|val|
|
143
|
+
case val
|
144
|
+
when Array
|
145
|
+
prepare_pg_text_array(val)
|
146
|
+
when nil
|
147
|
+
NULL
|
148
|
+
else
|
149
|
+
"\"#{val.to_s.gsub(/\\|"/){|s| ESCAPE_HASH[s]}}\""
|
150
|
+
end
|
151
|
+
}.join(',')
|
152
|
+
"{#{value}}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def _prepare_pg_string_array(value, &block)
|
156
|
+
value = value.map{|val|
|
157
|
+
case val
|
158
|
+
when Array
|
159
|
+
_prepare_pg_string_array(val, &block)
|
160
|
+
when nil
|
161
|
+
NULL
|
162
|
+
else
|
163
|
+
val = yield val
|
164
|
+
if val =~ /^'(.*)'$/m
|
165
|
+
"\"#{ $1.gsub(/\\|"/){|s| ESCAPE_HASH[s]} }\""
|
166
|
+
else
|
167
|
+
val
|
168
|
+
end
|
169
|
+
end
|
170
|
+
}.join(',')
|
171
|
+
"{#{value}}"
|
172
|
+
end
|
173
|
+
alias prepare_pg_string_array _prepare_pg_string_array
|
174
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def quote_bound_value_with_postgresql_arrays(value, c = connection)
|
5
|
+
if ::PGArrays::PgArray === value
|
6
|
+
c.quote_array_by_base_type(value, value.base_type)
|
7
|
+
else
|
8
|
+
quote_bound_value_without_postgresql_arrays(value, c)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
alias_method_chain :quote_bound_value, :postgresql_arrays
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module PGArrays
|
17
|
+
class PgArray < Array
|
18
|
+
attr_reader :base_type
|
19
|
+
|
20
|
+
def initialize(array, type=nil)
|
21
|
+
super(array)
|
22
|
+
@base_type = type if type
|
23
|
+
end
|
24
|
+
|
25
|
+
def base_type
|
26
|
+
@base_type || :other
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class PgAny < PgArray; end
|
31
|
+
class PgAll < PgArray; end
|
32
|
+
class PgIncludes < PgArray; end
|
33
|
+
|
34
|
+
if defined? CanCan::Ability
|
35
|
+
class PgAny
|
36
|
+
def include?(v)
|
37
|
+
Array === v && !( v & self ).empty?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class PgAll
|
42
|
+
def include?(v)
|
43
|
+
Array === v && (self - v).empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class PgIncludes < PgArray
|
48
|
+
def include?(v)
|
49
|
+
Array === v && (v - self).empty?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Array
|
56
|
+
|
57
|
+
def pg(type=nil)
|
58
|
+
::PGArrays::PgArray.new(self, type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def search_any(type=nil)
|
62
|
+
::PGArrays::PgAny.new(self, type)
|
63
|
+
end
|
64
|
+
|
65
|
+
def search_all(type=nil)
|
66
|
+
::PGArrays::PgAll.new(self, type)
|
67
|
+
end
|
68
|
+
|
69
|
+
def search_subarray(type=nil)
|
70
|
+
::PGArrays::PgIncludes.new(self, type)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Arel
|
2
|
+
module Nodes
|
3
|
+
class ArrayAny < Arel::Nodes::Binary
|
4
|
+
end
|
5
|
+
|
6
|
+
class ArrayAll < Arel::Nodes::Binary
|
7
|
+
end
|
8
|
+
|
9
|
+
class ArrayIncluded < Arel::Nodes::Binary
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Predications
|
14
|
+
def ar_any other
|
15
|
+
Nodes::ArrayAny.new self, other
|
16
|
+
end
|
17
|
+
|
18
|
+
def ar_all other
|
19
|
+
Nodes::ArrayAll.new self, other
|
20
|
+
end
|
21
|
+
|
22
|
+
def ar_included other
|
23
|
+
Nodes::ArrayIncluded.new self, other
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Visitors
|
28
|
+
class PostgreSQL
|
29
|
+
def visit_Arel_Nodes_ArrayAny o
|
30
|
+
"#{visit o.left} && #{visit o.right}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_Arel_Nodes_ArrayAll o
|
34
|
+
"#{visit o.left} @> #{visit o.right}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def visit_Arel_Nodes_ArrayIncluded o
|
38
|
+
"#{visit o.left} <@ #{visit o.right}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def visit_PGArrays_PgArray o
|
42
|
+
@connection.quote_array_by_base_type(o, o.base_type)
|
43
|
+
end
|
44
|
+
|
45
|
+
alias :visit_PGArrays_PgAny :visit_PGArrays_PgArray
|
46
|
+
alias :visit_PGArrays_PgAll :visit_PGArrays_PgArray
|
47
|
+
alias :visit_PGArrays_PgIncluded :visit_PGArrays_PgArray
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if ActiveRecord::VERSION::STRING < '3.1'
|
53
|
+
module ActiveRecord
|
54
|
+
class PredicateBuilder
|
55
|
+
def build_from_hash(attributes, default_table)
|
56
|
+
predicates = attributes.map do |column, value|
|
57
|
+
table = default_table
|
58
|
+
|
59
|
+
if value.is_a?(Hash)
|
60
|
+
table = Arel::Table.new(column, :engine => @engine)
|
61
|
+
build_from_hash(value, table)
|
62
|
+
else
|
63
|
+
column = column.to_s
|
64
|
+
|
65
|
+
if column.include?('.')
|
66
|
+
table_name, column = column.split('.', 2)
|
67
|
+
table = Arel::Table.new(table_name, :engine => @engine)
|
68
|
+
end
|
69
|
+
|
70
|
+
attribute = table[column] || Arel::Attribute.new(table, column)
|
71
|
+
|
72
|
+
case value
|
73
|
+
when PGArrays::PgAny
|
74
|
+
attribute.ar_any(value)
|
75
|
+
when PGArrays::PgAll
|
76
|
+
attribute.ar_all(value)
|
77
|
+
when PGArrays::PgIncludes
|
78
|
+
attribute.ar_included(value)
|
79
|
+
when PGArrays::PgArray
|
80
|
+
attribute.eq(value)
|
81
|
+
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
|
82
|
+
values = value.to_a.map { |x|
|
83
|
+
x.is_a?(ActiveRecord::Base) ? x.id : x
|
84
|
+
}
|
85
|
+
attribute.in(values)
|
86
|
+
when Range, Arel::Relation
|
87
|
+
attribute.in(value)
|
88
|
+
when ActiveRecord::Base
|
89
|
+
attribute.eq(value.id)
|
90
|
+
when Class
|
91
|
+
# FIXME: I think we need to deprecate this behavior
|
92
|
+
attribute.eq(value.name)
|
93
|
+
else
|
94
|
+
attribute.eq(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
predicates.flatten
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
module ActiveRecord
|
105
|
+
class PredicateBuilder
|
106
|
+
def self.build_from_hash(engine, attributes, default_table)
|
107
|
+
predicates = attributes.map do |column, value|
|
108
|
+
table = default_table
|
109
|
+
|
110
|
+
if value.is_a?(Hash)
|
111
|
+
table = Arel::Table.new(column, engine)
|
112
|
+
build_from_hash(engine, value, table)
|
113
|
+
else
|
114
|
+
column = column.to_s
|
115
|
+
|
116
|
+
if column.include?('.')
|
117
|
+
table_name, column = column.split('.', 2)
|
118
|
+
table = Arel::Table.new(table_name, engine)
|
119
|
+
end
|
120
|
+
|
121
|
+
attribute = table[column.to_sym]
|
122
|
+
|
123
|
+
case value
|
124
|
+
when PGArrays::PgAny
|
125
|
+
attribute.ar_any(value)
|
126
|
+
when PGArrays::PgAll
|
127
|
+
attribute.ar_all(value)
|
128
|
+
when PGArrays::PgIncludes
|
129
|
+
attribute.ar_included(value)
|
130
|
+
when PGArrays::PgArray
|
131
|
+
attribute.eq(value)
|
132
|
+
when ActiveRecord::Relation
|
133
|
+
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
134
|
+
attribute.in(value.arel.ast)
|
135
|
+
when Array, ActiveRecord::Associations::CollectionProxy
|
136
|
+
values = value.to_a.map { |x|
|
137
|
+
x.is_a?(ActiveRecord::Base) ? x.id : x
|
138
|
+
}
|
139
|
+
|
140
|
+
ranges, values = values.partition{|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
|
141
|
+
predicates = ranges.map{|range| attribute.in(range)}
|
142
|
+
|
143
|
+
predicates << if values.include?(nil)
|
144
|
+
values = values.compact
|
145
|
+
if values.empty?
|
146
|
+
attribute.eq nil
|
147
|
+
else
|
148
|
+
attribute.in(values.compact).or attribute.eq(nil)
|
149
|
+
end
|
150
|
+
else
|
151
|
+
attribute.in(values)
|
152
|
+
end
|
153
|
+
|
154
|
+
predicates.inject{|composite, predicate| composite.or(predicate)}
|
155
|
+
when Range, Arel::Relation
|
156
|
+
attribute.in(value)
|
157
|
+
when ActiveRecord::Base
|
158
|
+
attribute.eq(value.id)
|
159
|
+
when Class
|
160
|
+
# FIXME: I think we need to deprecate this behavior
|
161
|
+
attribute.eq(value.name)
|
162
|
+
else
|
163
|
+
attribute.eq(value)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
predicates.flatten
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|