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,154 @@
1
+ module PGArrays
2
+ module ReferencesBy
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
14
+
15
+ class RelationHolder < Struct.new(:relation, :field, :klass)
16
+
17
+ def referenced(obj)
18
+ ids = (obj[field] || []).map{|i| i.to_i}
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
151
+ end
152
+ end
153
+
154
+ ActiveRecord::Base.extend PGArrays::ReferencesBy::ClassMethods
@@ -0,0 +1,248 @@
1
+ require 'ar_jdbc_pg_array/parser'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ class PostgreSQLColumn
6
+ include PgArrayParser
7
+ extend PgArrayParser
8
+
9
+ BASE_TYPE_COLUMNS = Hash.new { |h, base_type|
10
+ base_column = new(nil, nil, base_type.to_s, true)
11
+ h[base_type] = h[base_column.type] = base_column
12
+ }
13
+ attr_reader :base_column
14
+
15
+ def initialize(name, default, sql_type = nil, null = true)
16
+ if sql_type =~ /^(.+)\[\]$/
17
+ @base_sql_type = $1
18
+ @base_column = BASE_TYPE_COLUMNS[@base_sql_type]
19
+ end
20
+
21
+ if Hash === name
22
+ super
23
+ else
24
+ super(nil, name, default, sql_type, null)
25
+ end
26
+ end
27
+
28
+ def simplified_type_with_postgresql_arrays(field_type)
29
+ if field_type =~ /^(.+)\[\]$/
30
+ :"#{simplified_type_without_postgresql_arrays($1)}_array"
31
+ else
32
+ simplified_type_without_postgresql_arrays(field_type)
33
+ end
34
+ end
35
+ alias_method_chain :simplified_type, :postgresql_arrays
36
+
37
+ def klass
38
+ if type.to_s =~ /_array$/
39
+ Array
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ def type_cast(value)
46
+ return nil if value.nil?
47
+ case type
48
+ when :integer_array, :float_array
49
+ string_to_num_array(value)
50
+ when :decimal_array, :date_array, :boolean_array
51
+ safe_string_to_array(value)
52
+ when :timestamp_array, :time_array, :datetime_array, :binary_array
53
+ string_to_array(value)
54
+ when :text_array, :string_array
55
+ string_to_text_array(value)
56
+ else super
57
+ end
58
+ end
59
+
60
+ def type_cast_code(var_name)
61
+ case type
62
+ when :integer_array, :float_array
63
+ "#{self.class.name}.string_to_num_array(#{var_name})"
64
+ when :decimal_array, :date_array, :boolean_array
65
+ "#{self.class.name}.safe_string_to_array(#{var_name}, #{@base_sql_type.inspect})"
66
+ when :timestamp_array, :time_array, :datetime_array, :binary_array
67
+ "#{self.class.name}.string_to_array(#{var_name}, #{@base_sql_type.inspect})"
68
+ when :text_array, :string_array
69
+ "#{self.class.name}.string_to_text_array(#{var_name})"
70
+ else super
71
+ end
72
+ end
73
+
74
+ def default
75
+ res = super
76
+ Array === res ? res.dup : res
77
+ end
78
+
79
+ def self._string_to_array(string)
80
+ return string unless string.is_a? String
81
+ return nil if string.empty?
82
+
83
+ yield
84
+ end
85
+
86
+ def _string_to_array(string)
87
+ return string unless string.is_a? String
88
+ return nil if string.empty?
89
+
90
+ yield
91
+ end
92
+
93
+ def safe_string_to_array(string)
94
+ _string_to_array(string) do
95
+ parse_safe_pgarray(string){|v| @base_column.type_cast(v)}
96
+ end
97
+ end
98
+
99
+ def self.safe_string_to_array(string, sql_type)
100
+ _string_to_array(string) do
101
+ base_column = BASE_TYPE_COLUMNS[sql_type]
102
+ parse_safe_pgarray(string){|v| base_column.type_cast(v)}
103
+ end
104
+ end
105
+
106
+ def string_to_array(string)
107
+ _string_to_array(string) do
108
+ parse_pgarray(string){|v| @base_column.type_cast(v)}
109
+ end
110
+ end
111
+
112
+ def self.string_to_array(string, sql_type)
113
+ _string_to_array(string) do
114
+ base_column = BASE_TYPE_COLUMNS[sql_type]
115
+ parse_pgarray(string){|v| base_column.type_cast(v)}
116
+ end
117
+ end
118
+
119
+ def string_to_num_array(string)
120
+ _string_to_array(string) do
121
+ parse_numeric_pgarray(string)
122
+ end
123
+ end
124
+
125
+ def self.string_to_num_array(string)
126
+ _string_to_array(string) do
127
+ parse_numeric_pgarray(string)
128
+ end
129
+ end
130
+
131
+ def string_to_text_array(string)
132
+ _string_to_array(string) do
133
+ parse_pgarray(string){|v| v}
134
+ end
135
+ end
136
+
137
+ def self.string_to_text_array(string)
138
+ _string_to_array(string) do
139
+ parse_pgarray(string){|v| v}
140
+ end
141
+ end
142
+ end
143
+
144
+ class PostgreSQLAdapter #:nodoc:
145
+ include PgArrayParser
146
+ def quote_with_postgresql_arrays(value, column = nil)
147
+ if Array === value && column && "#{column.type}" =~ /^(.+)_array$/
148
+ quote_array_by_base_type(value, $1, column)
149
+ else
150
+ quote_without_postgresql_arrays(value, column)
151
+ end
152
+ end
153
+ alias_method_chain :quote, :postgresql_arrays
154
+
155
+ def quote_array_by_base_type(value, base_type, column = nil)
156
+ case base_type.to_sym
157
+ when :integer, :float, :decimal, :boolean, :date, :safe, :datetime, :timestamp, :time
158
+ "'#{ prepare_array_for_arel_by_base_type(value, base_type) }'"
159
+ when :string, :text, :other
160
+ pa = prepare_array_for_arel_by_base_type(value, base_type)
161
+ "'#{ quote_string(pa) }'"
162
+ else
163
+ "'#{ prepare_pg_string_array(value, base_type, column) }'"
164
+ end
165
+ end
166
+
167
+ def prepare_array_for_arel_by_base_type(value, base_type)
168
+ case base_type.to_sym
169
+ when :integer
170
+ prepare_pg_integer_array(value)
171
+ when :float
172
+ prepare_pg_float_array(value)
173
+ when :string, :text, :other
174
+ prepare_pg_text_array(value)
175
+ when :datetime, :timestamp, :time
176
+ prepare_pg_string_array(value, base_type)
177
+ when :decimal, :boolean, :date, :safe
178
+ prepare_pg_safe_array(value)
179
+ else
180
+ raise "Unsupported array base type #{base_type} for arel"
181
+ end
182
+ end
183
+
184
+ def prepare_pg_string_array(value, base_type, column = nil)
185
+ base_column = if column
186
+ column.base_column
187
+ else
188
+ PostgreSQLColumn::BASE_TYPE_COLUMNS[base_type.to_sym]
189
+ end
190
+ _prepare_pg_string_array(value) { |v| quote_without_postgresql_arrays(v, base_column) }
191
+ end
192
+
193
+ NATIVE_DATABASE_TYPES.keys.each do |key|
194
+ unless key == :primary_key
195
+ base = NATIVE_DATABASE_TYPES[key].dup
196
+ base[:name] = base[:name] + '[]'
197
+ NATIVE_DATABASE_TYPES[:"#{key}_array"] = base
198
+ TableDefinition.class_eval <<-EOV
199
+ def #{key}_array(*args) # def string_array(*args)
200
+ options = args.extract_options! # options = args.extract_options!
201
+ column_names = args # column_names = args
202
+ #
203
+ column_names.each { |name| column(name, :'#{key}_array', options) } # column_names.each { |name| column(name, :string_array, options) }
204
+ end # end
205
+ EOV
206
+ Table.class_eval <<-EOV
207
+ def #{key}_array(*args) # def string_array(*args)
208
+ options = args.extract_options! # options = args.extract_options!
209
+ column_names = args # column_names = args
210
+ #
211
+ column_names.each { |name| # column_names.each { |name|
212
+ @base.add_column(@table_name, name, :'#{key}_array', options) # @base.add_column(@table_name, name, :string_array, options) }
213
+ } # }
214
+ end # end
215
+ EOV
216
+ end
217
+ end
218
+
219
+ def add_column_with_postgresql_arrays(table, column, type, options = {})
220
+ if type.to_s =~ /^(.+)_array$/ && options[:default].is_a?(Array)
221
+ options = options.merge(:default => prepare_array_for_arel_by_base_type(options[:default], $1))
222
+ end
223
+ add_column_without_postgresql_arrays(table, column, type, options)
224
+ end
225
+ alias_method_chain :add_column, :postgresql_arrays
226
+
227
+ def type_to_sql_with_postgresql_arrays(type, limit = nil, precision = nil, scale = nil)
228
+ if type.to_s =~ /^(.+)_array$/
229
+ type_to_sql_without_postgresql_arrays($1.to_sym, limit, precision, scale) + '[]'
230
+ else
231
+ type_to_sql_without_postgresql_arrays(type, limit, precision, scale)
232
+ end
233
+ end
234
+ alias_method_chain :type_to_sql, :postgresql_arrays
235
+
236
+ if method_defined?(:type_cast)
237
+ def type_cast_with_postgresql_arrays(value, column)
238
+ if Array === value && column && "#{column.type}" =~ /^(.+)_array$/
239
+ prepare_array_for_arel_by_base_type(value, $1)
240
+ else
241
+ type_cast_without_postgresql_arrays(value, column)
242
+ end
243
+ end
244
+ alias_method_chain :type_cast, :postgresql_arrays
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,47 @@
1
+ module Arel
2
+ module Attributes
3
+ %w{Integer Float Decimal Boolean String Time}.each do |basetype|
4
+ module_eval <<-"END"
5
+ class #{basetype}Array < Attribute
6
+ end
7
+ END
8
+ end
9
+ end
10
+
11
+ module Attributes
12
+ class << self
13
+ def for_with_postgresql_arrays(column)
14
+ if column.type.to_s =~ /^(.+)_array$/
15
+ ('Arel::Attributes::' + for_without_postgresql_arrays(column.base_column).name.split('::').last + 'Array').constantize
16
+ else
17
+ for_without_postgresql_arrays(column)
18
+ end
19
+ end
20
+ alias_method_chain :for, :postgresql_arrays
21
+ end
22
+ end
23
+ end
24
+
25
+ module ActiveRecord
26
+ class << Base
27
+ if method_defined?(:column_defaults)
28
+ alias column_defaults_without_extradup column_defaults
29
+ def column_defaults_with_extradup
30
+ res = {}
31
+ column_defaults_without_extradup.each{|k, v|
32
+ res[k] = Array === v ? v.dup : v
33
+ }
34
+ res
35
+ end
36
+ def column_defaults
37
+ defaults = column_defaults_without_extradup
38
+ if defaults.values.grep(Array).empty?
39
+ alias column_defaults column_defaults_without_extradup
40
+ else
41
+ alias column_defaults column_defaults_with_extradup
42
+ end
43
+ column_defaults
44
+ end
45
+ end
46
+ end
47
+ end