querybuilder 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.9.0 2010-09-14
2
+
3
+ * 1 Major enhancements
4
+ * Lazy loading of custom queries.
5
+ * Select keys introspection in Query class.
6
+ * Using new RubyLess.translate API.
7
+
1
8
  == 0.8.3 2010-08-31
2
9
 
3
10
  * 2 Major enhancements
data/README.rdoc CHANGED
@@ -43,7 +43,7 @@ xml/html environments):
43
43
 
44
44
  '+' | '-' | '<' | '<=' | '=' | '>=' | '>'
45
45
  'or' | 'and' | 'lt' | 'le' | 'eq' | 'ne' | 'ge' | 'gt'
46
- 'like' | 'not like' | 'match'
46
+ 'like' | 'not like' | 'match' | 'in'
47
47
 
48
48
  This lets you build complex queries like:
49
49
 
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ begin
16
16
  gem.email = "gaspard@teti.ch"
17
17
  gem.homepage = "http://zenadmin.org/524"
18
18
  gem.authors = ["Gaspard Bucher"]
19
- gem.add_dependency "rubyless", ">= 0.5.0"
19
+ gem.add_dependency "rubyless", ">= 0.7.0"
20
20
  gem.add_development_dependency "shoulda", ">= 0"
21
21
  gem.add_development_dependency "yamltest", ">= 0.5.0"
22
22
  gem.extensions << 'lib/extconf.rb'
@@ -1,3 +1,3 @@
1
1
  module QueryBuilder
2
- VERSION = '0.8.3'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -6,7 +6,7 @@ module QueryBuilder
6
6
 
7
7
  class << self
8
8
  # class variable
9
- attr_accessor :main_table, :main_class, :custom_queries
9
+ attr_accessor :main_table, :main_class, :custom_queries, :custom_query_files
10
10
  attr_accessor :defaults
11
11
  attr_accessor :before_process_callbacks, :after_process_callbacks
12
12
 
@@ -68,9 +68,16 @@ module QueryBuilder
68
68
  # Once loaded, this 'custom query' can be used in a query like:
69
69
  # "images from abc where a > 54"
70
70
  def load_custom_queries(directories)
71
+ # lazy loading (evaluation happens on first query)
72
+ self.custom_query_files ||= []
73
+ self.custom_query_files << directories
74
+ end
75
+
76
+ def load_custom_queries!
77
+ return unless list = self.custom_query_files
71
78
  klass = nil
72
79
  self.custom_queries ||= {}
73
- Dir.glob(directories).each do |dir|
80
+ Dir.glob(list.flatten).each do |dir|
74
81
  if File.directory?(dir)
75
82
  Dir.foreach(dir) do |file|
76
83
  next unless file =~ /(.+).yml$/
@@ -97,6 +104,7 @@ module QueryBuilder
97
104
  end
98
105
  end
99
106
  end
107
+ self.custom_query_files = nil
100
108
  rescue NameError => err
101
109
  raise ArgumentError.new("Invalid Processor class (#{klass})")
102
110
  end
@@ -427,7 +435,7 @@ module QueryBuilder
427
435
 
428
436
  def process_attr(fld_name)
429
437
  if @rubyless_helper
430
- insert_bind(RubyLess.translate(fld_name, @rubyless_helper))
438
+ insert_bind(RubyLess.translate(@rubyless_helper, fld_name))
431
439
  else
432
440
  insert_bind(fld_name)
433
441
  end
@@ -449,14 +457,14 @@ module QueryBuilder
449
457
 
450
458
  def process_dstring(string)
451
459
  raise QueryBuilder::SyntaxError.new("Cannot parse rubyless (missing binding context).") unless helper = @rubyless_helper
452
- res = RubyLess.translate_string(string, helper)
460
+ res = RubyLess.translate_string(helper, string)
453
461
  res.literal ? quote(res.literal) : insert_bind(res)
454
462
  end
455
463
 
456
464
  def process_rubyless(string)
457
465
  # compile RubyLess...
458
466
  raise QueryBuilder::SyntaxError.new("Cannot parse rubyless (missing binding context).") unless helper = @rubyless_helper
459
- res = RubyLess.translate(string, helper)
467
+ res = RubyLess.translate(helper, string)
460
468
  res.literal ? quote(res.literal) : insert_bind(res)
461
469
  end
462
470
 
@@ -656,13 +664,13 @@ module QueryBuilder
656
664
  end
657
665
  end
658
666
 
659
- def custom_query(relation)
667
+ def custom_query_without_loading(relation)
660
668
  return false unless first? && last? # current safety net until "from" is correctly implemented and tested
661
669
 
662
670
  custom_queries = self.class.custom_queries[self.class]
663
671
  if custom_queries &&
664
672
  custom_queries[@opts[:custom_query_group]] &&
665
- custom_query = custom_queries[@opts[:custom_query_group]][relation]
673
+ custom_query = custom_queries[@opts[:custom_query_group]][relation.singularize]
666
674
 
667
675
  custom_query.each do |k,v|
668
676
  @query.send(:instance_variable_set, "@#{k}", prepare_custom_query_arguments(k.to_sym, v))
@@ -675,6 +683,15 @@ module QueryBuilder
675
683
  end
676
684
  end
677
685
 
686
+ # Method executed only once, then alias resolves to custom_query_without_loading.
687
+ def custom_query(*args)
688
+ self.class.load_custom_queries!
689
+ self.class.class_eval do
690
+ alias custom_query custom_query_without_loading
691
+ end
692
+ custom_query_without_loading(*args)
693
+ end
694
+
678
695
  private
679
696
 
680
697
  %W{filter scope limit offset paginate group order}.each do |context|
@@ -53,6 +53,20 @@ module QueryBuilder
53
53
  @where << filter
54
54
  end
55
55
 
56
+ # Return all explicit selected keys (currently selection is only available in custom queries)
57
+ # For example, sql such as "SELECT form.*, MAX(form.date) AS last_date" would provice 'last_date' key.
58
+ def select_keys
59
+ @select_keys ||= (@select || []).map do |field|
60
+ if field =~ %r{AS\s+(.+)$}
61
+ $1
62
+ elsif field =~ %r{^(\w+\.|)([^\*]+)$}
63
+ $2
64
+ else
65
+ nil
66
+ end
67
+ end.compact
68
+ end
69
+
56
70
  # Convert query object to a string. This string should then be evaluated.
57
71
  #
58
72
  # ==== Parameters
@@ -285,4 +299,4 @@ module QueryBuilder
285
299
  end
286
300
  end
287
301
  end
288
- end
302
+ end
data/querybuilder.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{querybuilder}
8
- s.version = "0.8.3"
8
+ s.version = "0.9.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gaspard Bucher"]
12
- s.date = %q{2010-08-31}
12
+ s.date = %q{2010-09-14}
13
13
  s.description = %q{QueryBuilder is an interpreter for the "pseudo sql" language. This language
14
14
  can be used for two purposes:
15
15
 
@@ -86,16 +86,16 @@ Gem::Specification.new do |s|
86
86
  s.specification_version = 3
87
87
 
88
88
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
89
- s.add_runtime_dependency(%q<rubyless>, [">= 0.5.0"])
89
+ s.add_runtime_dependency(%q<rubyless>, [">= 0.7.0"])
90
90
  s.add_development_dependency(%q<shoulda>, [">= 0"])
91
91
  s.add_development_dependency(%q<yamltest>, [">= 0.5.0"])
92
92
  else
93
- s.add_dependency(%q<rubyless>, [">= 0.5.0"])
93
+ s.add_dependency(%q<rubyless>, [">= 0.7.0"])
94
94
  s.add_dependency(%q<shoulda>, [">= 0"])
95
95
  s.add_dependency(%q<yamltest>, [">= 0.5.0"])
96
96
  end
97
97
  else
98
- s.add_dependency(%q<rubyless>, [">= 0.5.0"])
98
+ s.add_dependency(%q<rubyless>, [">= 0.7.0"])
99
99
  s.add_dependency(%q<shoulda>, [">= 0"])
100
100
  s.add_dependency(%q<yamltest>, [">= 0.5.0"])
101
101
  end
@@ -11,17 +11,32 @@ DummyProcessor:
11
11
  - '2'
12
12
  - '3'
13
13
  order: a ASC
14
-
14
+
15
+ star:
16
+ select:
17
+ - test.*
18
+ - '*'
19
+ - a
20
+ - 34 AS number
21
+ - c
22
+ tables:
23
+ - test
24
+ where:
25
+ - '1'
26
+ - '2'
27
+ - '3'
28
+ order: a ASC
29
+
15
30
  two_table:
16
31
  main_table: 'table_one'
17
32
  select:
18
33
  - x AS x
19
34
  - IF(table_one.y,table_one.y,table_two.z) AS y
20
35
  - table_two.name
21
- tables:
36
+ tables:
22
37
  - table_two
23
38
  - table_one
24
-
39
+
25
40
  two_table_main:
26
41
  main_table: foo
27
42
  select:
@@ -67,6 +67,20 @@ class DummyQueryBuilder < Test::Unit::TestCase
67
67
  end
68
68
  end # Including QueryBuilder
69
69
 
70
+ context 'A query with custom select' do
71
+ subject do
72
+ DummyProcessor.new('star where number < 4', :custom_query_group => 'test').query
73
+ end
74
+
75
+ should 'respond to select_keys' do
76
+ assert_equal %w{a number c}, subject.select_keys
77
+ end
78
+
79
+ should 'not include star keys' do
80
+ assert !subject.select_keys.include?('*')
81
+ end
82
+ end # A query with custom select
83
+
70
84
 
71
85
  def yt_parse(key, source, opts)
72
86
  opts = {:rubyless_helper => self}.merge(Hash[*(opts.map{|k,v| [k.to_sym, v]}.flatten)])
@@ -114,4 +128,4 @@ class DummyQueryBuilder < Test::Unit::TestCase
114
128
  end
115
129
  =end
116
130
  yt_make
117
- end
131
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 8
8
- - 3
9
- version: 0.8.3
7
+ - 9
8
+ - 0
9
+ version: 0.9.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Gaspard Bucher
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-31 00:00:00 +02:00
17
+ date: 2010-09-14 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -26,9 +26,9 @@ dependencies:
26
26
  - !ruby/object:Gem::Version
27
27
  segments:
28
28
  - 0
29
- - 5
29
+ - 7
30
30
  - 0
31
- version: 0.5.0
31
+ version: 0.7.0
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency