rquerypad 0.1.20 → 0.1.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/CHANGELOG +3 -0
  2. data/Rakefile +1 -1
  3. data/lib/rquerypad.rb +249 -231
  4. metadata +2 -2
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ Changes in version 0.1.21 (2009-4-14)
2
+ rquerypad will not change original options to avoid side effect
3
+
1
4
  Changes in version 0.1.20 (2009-3-9)
2
5
  ------------------------------------
3
6
  Support rails 2.3
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
 
7
7
  PKG_NAME = "rquerypad"
8
- PKG_VERSION = "0.1.20"
8
+ PKG_VERSION = "0.1.21"
9
9
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
10
10
  PKG_FILES = FileList[
11
11
  '[A-Z]*',
data/lib/rquerypad.rb CHANGED
@@ -3,15 +3,15 @@ author: Leon Li(scorpio_leon@hotmail.com)
3
3
  =end
4
4
  require 'md5'
5
5
  class Rquerypad
6
- class << self
6
+ class << self
7
7
 
8
- def prepare_with_debug
9
- prepare(true)
10
- end
11
- def prepare(debug = $RQUERYPAD_DEBUG)
12
- return nil unless @prepared.nil?
13
- @prepared = true
14
- ActiveRecord::Base.class_eval %{
8
+ def prepare_with_debug
9
+ prepare(true)
10
+ end
11
+ def prepare(debug = $RQUERYPAD_DEBUG)
12
+ return nil unless @prepared.nil?
13
+ @prepared = true
14
+ ActiveRecord::Base.class_eval %{
15
15
  class << self
16
16
  def group_tables(options)
17
17
  group = [options[:group], scope(:find, :group) ].join(", ")
@@ -25,8 +25,8 @@ class Rquerypad
25
25
  include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options) || include_eager_group?(options)
26
26
  end
27
27
  end
28
- }
29
- ActiveRecord::Base.class_eval %{
28
+ }
29
+ ActiveRecord::Base.class_eval %{
30
30
  require "rquerypad"
31
31
 
32
32
  class << self
@@ -44,7 +44,7 @@ class Rquerypad
44
44
  #p "rquerypad_cache[cache_key]: #\{rquerypad_cache[cache_key]\}"
45
45
  if rquerypad_cache[cache_key].nil?
46
46
  #{"p 'process for:', options" if debug}
47
- Rquerypad.new(self).improve_options!(options)
47
+ options = Rquerypad.new(self).improve_options!(options)
48
48
  rquerypad_cache[cache_key] = Marshal.dump(options)
49
49
  #p "rquerypad_cache[cache_key]: #\{rquerypad_cache[cache_key]\}"
50
50
  else
@@ -66,9 +66,9 @@ class Rquerypad
66
66
  @@rquerypad_cache
67
67
  end
68
68
  end
69
- }
69
+ }
70
70
 
71
- ActiveRecord::Calculations.class_eval %{
71
+ ActiveRecord::Calculations.class_eval %{
72
72
  require "rquerypad"
73
73
  module ClassMethods
74
74
  @@rquerypad_cache = {}
@@ -80,7 +80,7 @@ class Rquerypad
80
80
  cache_key = Rquerypad.options_key(self, options)
81
81
  if rquerypad_cache[cache_key].nil?
82
82
  #{"p 'process for:', options" if debug}
83
- Rquerypad.new(self).improve_options!(options)
83
+ options = Rquerypad.new(self).improve_options!(options)
84
84
  rquerypad_cache[cache_key] = Marshal.dump(options)
85
85
  else
86
86
  options = Marshal.load(rquerypad_cache[cache_key])
@@ -97,251 +97,269 @@ class Rquerypad
97
97
  @@rquerypad_cache
98
98
  end
99
99
  end
100
- }
100
+ }
101
101
 
102
- Hash.class_eval do
103
- def single_lt?(other)
104
- return false if self == other
105
- return true if self.size == 0
106
- return false unless other.is_a?(Hash)
107
- return false if other.nil? || other.size < self.size
108
- return false if self.size != other.size || self.size > 1
109
- a = self.to_a[0]
110
- o = other.to_a[0]
111
- return false if a[0] != o[0]
112
- return true unless a[1].is_a?(Hash)
113
- a[1].single_lt?(o[1])
102
+ Hash.class_eval do
103
+ def single_lt?(other)
104
+ return false if self == other
105
+ return true if self.size == 0
106
+ return false unless other.is_a?(Hash)
107
+ return false if other.nil? || other.size < self.size
108
+ return false if self.size != other.size || self.size > 1
109
+ a = self.to_a[0]
110
+ o = other.to_a[0]
111
+ return false if a[0] != o[0]
112
+ return true unless a[1].is_a?(Hash)
113
+ a[1].single_lt?(o[1])
114
+ end
115
+ def single_gt?(other)
116
+ return false if self == other
117
+ return !(single_lt?(other))
118
+ end
119
+ end
114
120
  end
115
- def single_gt?(other)
116
- return false if self == other
117
- return !(single_lt?(other))
121
+
122
+ def process_inner_join(sql, inner_joins)
123
+ inner_joins.each {|i| sql.gsub!(/LEFT OUTER JOIN [`]?#{i}[`]?/, "INNER JOIN #{i}")} unless inner_joins.empty?
124
+ sql
118
125
  end
119
- end
120
- end
121
126
 
122
- def process_inner_join(sql, inner_joins)
123
- inner_joins.each {|i| sql.gsub!(/LEFT OUTER JOIN [`]?#{i}[`]?/, "INNER JOIN #{i}")} unless inner_joins.empty?
124
- sql
127
+ def options_key(obj, options)
128
+ key = obj.to_s + options.to_a.to_s
129
+ key = MD5.hexdigest(key) if key.length > 100
130
+ key
131
+ end
125
132
  end
126
-
127
- def options_key(obj, options)
128
- key = obj.to_s + options.to_a.to_s
129
- key = MD5.hexdigest(key) if key.length > 100
130
- key
133
+ def initialize(obj)
134
+ @owner = obj
135
+ @class_name = obj.to_s.scan(/(\w*::)*([^\(]*)/)[0][1]
136
+ # #@table_name = @class_name.pluralize.underscore
137
+ @table_name = obj.table_name
138
+ @tnwithdot = @table_name + "."
139
+ @new_include = []
140
+ @old_include = nil
141
+ @inner_joins = []
131
142
  end
132
- end
133
- def initialize(obj)
134
- @owner = obj
135
- @class_name = obj.to_s.scan(/(\w*::)*([^\(]*)/)[0][1]
136
- #@table_name = @class_name.pluralize.underscore
137
- @table_name = obj.table_name
138
- @tnwithdot = @table_name + "."
139
- @new_include = []
140
- @old_include = nil
141
- @inner_joins = []
142
- end
143
143
 
144
- def improve_options!(option_hash)
145
-
146
- option_hash.each do |key, value|
147
- next if value.blank?
148
- key_str = key.to_s
149
- if key_str == "conditions"
150
- option_hash[key] = improve_conditions(value)
151
- elsif (key_str == "order" || key_str == "group" || key_str == "group_field")
152
- option_hash[key] = improve_ordergroup(value).join(", ")
153
- elsif (key_str == "include")
154
- @old_include = value
155
- end
156
- end
157
- return nil if @new_include.empty?
158
- #generate new include
144
+ def improve_options!(option_hash)
145
+ new_options = {}
146
+ option_hash.each do |key, value|
147
+ if value.blank?
148
+ new_options[key] = value
149
+ next
150
+ end
151
+ key_str = key.to_s
152
+ if key_str == "conditions"
153
+ new_options[key] = improve_conditions(value)
154
+ elsif (key_str == "order" || key_str == "group" || key_str == "group_field")
155
+ new_options[key] = improve_ordergroup(value).join(", ")
156
+ else
157
+ new_options[key] = value
158
+ if (key_str == "include")
159
+ @old_include = value
160
+ end
161
+ end
162
+ end
163
+ return new_options if @new_include.empty?
164
+ # #generate new include
159
165
 
160
- remove_dup_includes
161
- unless @new_include.nil?
162
- option_hash[:include] = [] if @old_include.nil?
163
- @new_include += @old_include unless @old_include.nil?
164
- option_hash.each do |key, value|
165
- if key.to_s == "include"
166
- option_hash[key] = @new_include
166
+ remove_dup_includes
167
+ unless @new_include.nil?
168
+ new_options[:include] ||= []
169
+ new_options[:include] += @new_include
170
+ new_options[:include].uniq!
167
171
  end
168
- end
172
+ new_options[:inner_joins] = @inner_joins unless @inner_joins.empty?
173
+ new_options
169
174
  end
170
- option_hash[:inner_joins] = @inner_joins unless @inner_joins.empty?
171
- end
172
175
 
173
- def remove_dup_includes
174
- final_includes = []
175
- @new_include.each do |ni|
176
- next if final_includes.include?(ni)
177
- if final_includes.size > 0
178
- final_includes.each_index do |i|
179
- if ni.is_a?(Hash)
180
- if final_includes[i].is_a?(Hash)
181
- if final_includes[i].single_lt?(ni)
182
- final_includes[i] = ni
183
- elsif final_includes[i].single_gt?(ni)
184
- else
185
- final_includes << ni
186
- end
187
- elsif ni.entries[0][0].to_s == final_includes[i].to_s
188
- final_includes[i] = ni
176
+ def remove_dup_includes
177
+ final_includes = []
178
+ @new_include.each do |ni|
179
+ next if final_includes.include?(ni)
180
+ if final_includes.size > 0
181
+ final_includes.each_index do |i|
182
+ if ni.is_a?(Hash)
183
+ if final_includes[i].is_a?(Hash)
184
+ if final_includes[i].single_lt?(ni)
185
+ final_includes[i] = ni
186
+ elsif final_includes[i].single_gt?(ni)
187
+ else
188
+ final_includes << ni
189
+ end
190
+ elsif ni.entries[0][0].to_s == final_includes[i].to_s
191
+ final_includes[i] = ni
192
+ else
193
+ final_includes << ni
194
+ end
195
+ else
196
+ if final_includes[i].is_a?(Hash)
197
+ final_includes << ni if final_includes[i].entries[0][0].to_s != ni.to_s
198
+ else
199
+ final_includes << ni if final_includes[i].to_s != ni.to_s
200
+ end
201
+ end
202
+ end
189
203
  else
190
- final_includes << ni
191
- end
192
- else
193
- if final_includes[i].is_a?(Hash)
194
- final_includes << ni if final_includes[i].entries[0][0].to_s != ni.to_s
195
- else
196
- final_includes << ni if final_includes[i].to_s != ni.to_s
204
+ final_includes << ni
197
205
  end
198
- end
199
206
  end
200
- else
201
- final_includes << ni
202
- end
207
+ @new_include = final_includes
203
208
  end
204
- @new_include = final_includes
205
- end
206
209
 
207
- def improve_conditions(options)
208
-
209
- if options.is_a?(Hash)
210
- #work around frozen issue
211
- new_options = {}
212
- until options.empty?
213
- key, value = options.shift
214
- key = key.to_s.dup
215
- new_options[process_single!(key)] = value
216
- end
217
- options.merge!(new_options)
218
- else
219
- if options.is_a?(String)
220
- options = [options]
221
- end
222
- str = options[0]
223
- return nil if str.nil?
224
- qp = /['"][^."]*['"]/
225
- temp = str.scan(qp)
226
- #replace string in quote to avoid unecessary processing
227
- str.gsub!(qp, "'[??]'") unless temp.empty?
228
- str.gsub!(/(([\w\(\)]+\.)+\w+)[ !><=]/) do |n|
229
- #cut last char and abstract association for include
230
- if n =~ /^\(/
231
- '(' + abstract_association(n[1..-2]) + n[-1, 1]
210
+ def improve_conditions(options)
211
+ new_options = nil
212
+ if options.is_a?(Hash)
213
+ # #work around frozen issue
214
+ new_options = {}
215
+ until options.empty?
216
+ key, value = options.shift
217
+ new_options[process_single(key.to_s)] = value
218
+ end
232
219
  else
233
- abstract_association(n[0..-2]) + n[-1, 1]
234
- end
220
+ if options.is_a?(String)
221
+ new_options = [options]
222
+ else
223
+ new_options = options.dup
224
+ end
225
+ str = new_options[0]
226
+ return nil if str.nil?
227
+ qp = /['"][^."]*['"]/
228
+ temp = str.scan(qp)
229
+ # #replace string in quote to avoid unecessary processing
230
+ str = str.gsub(qp, "'[??]'") unless temp.empty?
231
+ str = str.gsub(/(([\w\(\)]+\.)+\w+)[ !><=]/) do |n|
232
+ # #cut last char and abstract association for include
233
+ if n =~ /^\(/
234
+ '(' + abstract_association(n[1..-2]) + n[-1, 1]
235
+ else
236
+ abstract_association(n[0..-2]) + n[-1, 1]
237
+ end
235
238
 
236
- end
237
- #add @table_name on single field
238
- str.gsub!(/[\.:]?[`"']?\w+[`"']?[\.]?/) {|x| (x =~ /\D+/).nil? || x[-1, 1] == "." || x[0, 1] == ":" || ["and", "or", "is", "null", "not", "like", "in"].include?(x.downcase) ? x : @tnwithdot + x}
239
- str.gsub!(/\.#{@table_name}\./, ".")
240
- #recover string in quote
241
- unless temp.empty?
242
- i = -1
243
- str.gsub!(/\'\[\?\?\]\'/) do
244
- i += 1
245
- temp[i]
239
+ end
240
+ # #add @table_name on single field
241
+ str = str.gsub(/[\.:]?[`"']?\w+[`"']?[\.]?/) {|x| (x =~ /\D+/).nil? || x[-1, 1] == "." || x[0, 1] == ":" || ["and", "or", "is", "null", "not", "like", "in"].include?(x.downcase) ? x : @tnwithdot + x}
242
+ str = str.gsub(/\.#{@table_name}\./, ".")
243
+ # #recover string in quote
244
+ unless temp.empty?
245
+ i = -1
246
+ str = str.gsub(/\'\[\?\?\]\'/) do
247
+ i += 1
248
+ temp[i]
249
+ end
250
+ end
251
+ new_options[0] = str
246
252
  end
247
- end
248
- options[0] = str
253
+ new_options
249
254
  end
250
- options
251
- end
252
255
 
253
- def process_single!(key)
254
- return key.sub!('=', '') if key[0, 1] == '='
255
- if key.include?(".")
256
- if /^(.+)[ ]/ =~ key
257
- key.sub!("#$1", abstract_association("#$1"))
258
- else
259
- key.sub!(/^.+$/, abstract_association(key))
260
- end
261
- else
262
- key.sub!(/^.+$/, @tnwithdot + key)
256
+ def process_single(key)
257
+ return key.sub('=', '') if key[0, 1] == '='
258
+ if key.include?(".")
259
+ if /^(.+)[ ]/ =~ key
260
+ key = key.sub("#$1", abstract_association("#$1"))
261
+ else
262
+ key = key.sub(/^.+$/, abstract_association(key))
263
+ end
264
+ else
265
+ key = key.sub(/^.+$/, @tnwithdot + key)
266
+ end
267
+ key
263
268
  end
264
- end
265
269
 
266
- def improve_ordergroup(fields)
267
- fields = [fields] if fields.is_a?(String)
268
- fields.each {|field| process_single!(field)}
269
- fields
270
- end
271
-
272
- def abstract_association(str)
273
- result = nil
274
- owner = @owner
275
- names = str.split(".")
276
- return str if names.size == 2 && names[0] == @table_name
277
- #seperate assocations/tables and field
278
- tables, field = names[0..-2], names[-1]
279
- owners = []
280
- #get relevant owner for each table
281
- tables.each_index do |i|
282
- #tables[i] = tables[i].pluralize
283
- if i == 0
284
- owners[i] = owner
285
- else
286
- tname = cut_end_underscore(tables[i-1])
287
- r = owners[i-1].reflections[tname.to_sym].options
288
- owners[i] = r[:class_name].nil? ? eval(owners[i-1].to_s.gsub(/\w*$/, "")+tname.singularize.camelize) : eval("#{r[:class_name]}")
289
- end
290
- end
291
- owners.reverse!
292
- tables.reverse!
293
- tables.each_index do |i|
294
- if tables[i][-1, 1] == "_"
295
- tables[i].reverse!.sub!("_", "").reverse!
296
- @inner_joins << transfer_table_name(tables[i], owners[i])
297
- end
298
- end
299
- #process special id field in a belongs_to association
300
- if field == "id"
301
- if owners[0].reflections[tables[0].to_sym].macro.to_s == "belongs_to"
302
- result = transfer_table_id(tables[0], owners[0])
303
- @inner_joins.delete(tables[0]) if @inner_joins.include?(tables[0])
304
- unless owners[1].nil?
305
- result = transfer_table_name(tables[1], owners[1]) + "." + result
270
+ def improve_ordergroup(fields)
271
+ result = []
272
+ if fields.is_a?(Array)
273
+ result += fields
274
+ else
275
+ result << fields
306
276
  end
307
- tables.delete_at(0)
308
- owners.delete_at(0)
309
- return result if tables.empty?
310
- end
277
+ result.each_index {|i| result[i] = process_single(result[i])}
278
+ result
311
279
  end
280
+
281
+ def abstract_association(str)
282
+ result = nil
283
+ owner = @owner
284
+ names = str.split(".")
285
+ return str if names.size == 2 && names[0] == @table_name
286
+ # #seperate assocations/tables and field
287
+ tables, field = names[0..-2], names[-1]
288
+ owners = []
289
+ # #get relevant owner for each table
290
+ tables.each_index do |i|
291
+ # #tables[i] = tables[i].pluralize
292
+ if i == 0
293
+ owners[i] = owner
294
+ else
295
+ tname = cut_end_underscore(tables[i-1])
296
+ r = owners[i-1].reflections[tname.to_sym].options
297
+ owners[i] = r[:class_name].nil? ? eval(owners[i-1].to_s.gsub(/\w*$/, "")+tname.singularize.camelize) : eval("#{r[:class_name]}")
298
+ end
299
+ end
300
+ owners.reverse!
301
+ tables.reverse!
302
+ tables.each_index do |i|
303
+ if tables[i][-1, 1] == "_"
304
+ tables[i].reverse!.sub!("_", "").reverse!
305
+ @inner_joins << transfer_table_name(tables[i], owners[i])
306
+ end
307
+ end
308
+ # #process special id field in a belongs_to association
309
+ if field == "id"
310
+ if owners[0].reflections[tables[0].to_sym].macro.to_s == "belongs_to"
311
+ result = transfer_table_id(tables[0], owners[0])
312
+ @inner_joins.delete(tables[0]) if @inner_joins.include?(tables[0])
313
+ unless owners[1].nil?
314
+ result = transfer_table_name(tables[1], owners[1]) + "." + result
315
+ end
316
+ tables.delete_at(0)
317
+ owners.delete_at(0)
318
+ return result if tables.empty?
319
+ end
320
+ end
312
321
 
313
- #get include
314
- if tables.length == 1 && tables[0] != @table_name
315
- @new_include << tables[0].to_sym unless @new_include.include?(tables[0].to_sym)
316
- else
317
- tables_clone = tables[0..-1]
318
- value = tables_clone.shift.to_sym
319
- until tables_clone.empty?
320
- hashes = {}
321
- hashes[tables_clone.shift.to_sym] = value
322
- value = hashes
323
- end
324
- @new_include << hashes unless hashes.nil? || @new_include.include?(hashes)
325
- end
326
- result ||= transfer_table_name(tables[0], owners[0]) + "." + field
322
+ # #get include
323
+ if tables.length == 1 && tables[0] != @table_name
324
+ @new_include << tables[0].to_sym unless @new_include.include?(tables[0].to_sym)
325
+ else
326
+ tables_clone = tables[0..-1]
327
+ value = tables_clone.shift.to_sym
328
+ until tables_clone.empty?
329
+ hashes = {}
330
+ hashes[tables_clone.shift.to_sym] = value
331
+ value = hashes
332
+ end
333
+ @new_include << hashes unless hashes.nil? || @new_include.include?(hashes)
334
+ end
335
+ result ||= transfer_table_name(tables[0], owners[0]) + "." + field
327
336
 
328
- end
337
+ end
338
+
339
+ def raise_error(name, owner)
340
+ error_message = "Rquerypad error\n"
341
+ error_message << "owner.reflections:\n"
342
+ error_message << owner.reflections.inspect
343
+ error_message << '\n\nnot found by key:' << name.to_sym.to_s
344
+ raise error_message
345
+ end
329
346
 
330
- def transfer_table_name(name, owner = @owner)
331
- #p "=======================================", name,"=======================================", owner.inspect, "=======================================", owner.reflections.inspect, "=======================================" if owner.reflections[name.to_sym].nil?
332
- owner.reflections[name.to_sym].class_name.gsub(/([\w]+::)*/, "").pluralize.underscore
333
- end
347
+ def transfer_table_name(name, owner = @owner)
348
+ raise_error(name, owner) if owner.reflections[name.to_sym].nil?
349
+ owner.reflections[name.to_sym].class_name.gsub(/([\w]+::)*/, "").pluralize.underscore
350
+ end
334
351
 
335
- def transfer_table_id(name, owner = @owner)
336
- owner.reflections[name.to_sym].class_name.gsub(/([\w]+::)*/, "").underscore + "_id"
337
- end
352
+ def transfer_table_id(name, owner = @owner)
353
+ raise_error(name, owner) if owner.reflections[name.to_sym].nil?
354
+ owner.reflections[name.to_sym].class_name.gsub(/([\w]+::)*/, "").underscore + "_id"
355
+ end
338
356
 
339
- def cut_end_underscore(str)
340
- str = str.reverse.sub("_", "").reverse if str[-1, 1] == "_"
341
- str
342
- end
357
+ def cut_end_underscore(str)
358
+ str = str.reverse.sub("_", "").reverse if str[-1, 1] == "_"
359
+ str
360
+ end
343
361
 
344
- def cut_end_underscore!(str)
345
- str.reverse!.sub!("_", "").reverse! if str[-1, 1] == "_"
346
- end
347
- end
362
+ def cut_end_underscore!(str)
363
+ str.reverse!.sub!("_", "").reverse! if str[-1, 1] == "_"
364
+ end
365
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rquerypad
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.20
4
+ version: 0.1.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leon Li
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-09 00:00:00 +08:00
12
+ date: 2009-04-15 00:00:00 +08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15