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.
@@ -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