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 +7 -0
- data/README.rdoc +1 -1
- data/Rakefile +1 -1
- data/lib/query_builder/info.rb +1 -1
- data/lib/query_builder/processor.rb +24 -7
- data/lib/query_builder/query.rb +15 -1
- data/querybuilder.gemspec +5 -5
- data/test/mock/custom_queries/test.yml +18 -3
- data/test/querybuilder_test.rb +15 -1
- metadata +6 -6
data/History.txt
CHANGED
data/README.rdoc
CHANGED
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.
|
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'
|
data/lib/query_builder/info.rb
CHANGED
@@ -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(
|
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(
|
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(
|
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(
|
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
|
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|
|
data/lib/query_builder/query.rb
CHANGED
@@ -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
|
+
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-
|
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.
|
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.
|
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.
|
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:
|
data/test/querybuilder_test.rb
CHANGED
@@ -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
|
-
-
|
9
|
-
version: 0.
|
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-
|
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
|
-
-
|
29
|
+
- 7
|
30
30
|
- 0
|
31
|
-
version: 0.
|
31
|
+
version: 0.7.0
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|