ar_jdbc_pg_array 0.1.0-java
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/.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
|