rquerypad 0.1.20 → 0.1.21

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