yql-query 1.0.0
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.
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/License.md +20 -0
- data/Rakefile +21 -0
- data/Readme.md +94 -0
- data/lib/yql-query.rb +1 -0
- data/lib/yql_query.rb +4 -0
- data/lib/yql_query/builder.rb +244 -0
- data/lib/yql_query/query.rb +70 -0
- data/lib/yql_query/source.rb +17 -0
- data/lib/yql_query/version.rb +4 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/yql_query_builder_spec.rb +330 -0
- data/spec/yql_query_source_spec.rb +11 -0
- data/yql-query.gemspec +30 -0
- metadata +150 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/License.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Steve Agalloco
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
task :test => :spec
|
9
|
+
|
10
|
+
namespace :doc do
|
11
|
+
require 'yard'
|
12
|
+
YARD::Rake::YardocTask.new do |task|
|
13
|
+
task.files = ['lib/**/*.rb']
|
14
|
+
task.options = [
|
15
|
+
'--protected',
|
16
|
+
'--output-dir', 'doc/yard',
|
17
|
+
'--markup', 'markdown',
|
18
|
+
'--readme', 'Readme.md'
|
19
|
+
]
|
20
|
+
end
|
21
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
YqlQuery
|
2
|
+
========
|
3
|
+
|
4
|
+
A simple [YQL query](http://developer.yahoo.com/yql/guide/index.html) generation library written in ruby, providing a chainable query builder capable of generating the most complex query conditions you can throw at it.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
(sudo) gem install yql-query
|
10
|
+
|
11
|
+
Documentation
|
12
|
+
-------------
|
13
|
+
|
14
|
+
[http://rdoc.info/gems/yql-query](http://rdoc.info/gems/yql-query)
|
15
|
+
|
16
|
+
Usage
|
17
|
+
-----
|
18
|
+
|
19
|
+
yql-query's primary interface is the Builder class. Once you've instantiated a builder, you can pretty much generate whatever query you'd like using it.
|
20
|
+
|
21
|
+
builder = YqlQuery::Builder.new
|
22
|
+
builder.table('music.artists')
|
23
|
+
|
24
|
+
to generate a query, the Builder class provides a number of methods to construct it's arguments:
|
25
|
+
|
26
|
+
builder.table('music.artists')
|
27
|
+
builder.select('name, genre')
|
28
|
+
builder.conditions("bands = 'false'")
|
29
|
+
builder.sort('age')
|
30
|
+
|
31
|
+
conditions are also aliased as 'where':
|
32
|
+
|
33
|
+
#using 'where'
|
34
|
+
builder.where("bands = 'true'")
|
35
|
+
|
36
|
+
# is the same thing as
|
37
|
+
builder.conditions("bands = 'true'")
|
38
|
+
|
39
|
+
conditions can be passed as either a string, an array or
|
40
|
+
|
41
|
+
builder.conditions(["name = 'Erykah Badu'", "release_year > '2005'"])
|
42
|
+
|
43
|
+
methods can be chained together:
|
44
|
+
|
45
|
+
builder.table('music.artists').select('name').where("genre = 'jazz'")
|
46
|
+
|
47
|
+
to generate the query, just call to_s or to_query:
|
48
|
+
|
49
|
+
builder.to_s
|
50
|
+
# => 'select * from tablename...'
|
51
|
+
|
52
|
+
builder.to_query
|
53
|
+
# => 'select * from tablename...'
|
54
|
+
|
55
|
+
passing a hash with a Builder instance to creates sub-select:
|
56
|
+
|
57
|
+
guid_query = Builder.new.table('users').select('guid').where("role = 'admin'")
|
58
|
+
|
59
|
+
builder = Builder.new.table('actions).where(:guid => guid_query)
|
60
|
+
builder.to_s
|
61
|
+
# => "select * from actions where guid in (select guid from users where role = 'admin')"
|
62
|
+
|
63
|
+
The full list of methods available:
|
64
|
+
|
65
|
+
table('music.albums')
|
66
|
+
use('http://somedomain.com/table.xml', 'othersource')
|
67
|
+
select('name')
|
68
|
+
conditions("genre = 'jazz'")
|
69
|
+
sort('albumName')
|
70
|
+
sort_descending('albumName')
|
71
|
+
limit(5)
|
72
|
+
offset(10)
|
73
|
+
tail(5)
|
74
|
+
truncate(10)
|
75
|
+
unique('format')
|
76
|
+
sanitize('description')
|
77
|
+
|
78
|
+
Refer to the [documentation](http://rdoc.info/gems/yql-query) for complete usage and more examples.
|
79
|
+
|
80
|
+
Note on Patches/Pull Requests
|
81
|
+
-----------------------------
|
82
|
+
|
83
|
+
* Fork the project.
|
84
|
+
* Make your feature addition or bug fix.
|
85
|
+
* Add tests for it. This is important so I don't break it in a
|
86
|
+
future version unintentionally.
|
87
|
+
* Commit, do not mess with rakefile, version, or history.
|
88
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
89
|
+
* Send me a pull request. Bonus points for topic branches.
|
90
|
+
|
91
|
+
Copyright
|
92
|
+
---------
|
93
|
+
|
94
|
+
Copyright (c) 2011 Steve Agalloco. See LICENSE for details.
|
data/lib/yql-query.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'yql_query'
|
data/lib/yql_query.rb
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
module YqlQuery
|
2
|
+
|
3
|
+
# The primary query builder class, wraps a {Query} object and provides methods to assign query arguments.
|
4
|
+
class Builder
|
5
|
+
|
6
|
+
attr_accessor :query
|
7
|
+
|
8
|
+
# Instantiates a new Builder instance.
|
9
|
+
# @param
|
10
|
+
def initialize(options={})
|
11
|
+
self.query = Query.new
|
12
|
+
self.query.table = options.delete(:table)
|
13
|
+
self.query.limit = options.delete(:limit)
|
14
|
+
self.query.offset = options.delete(:offset)
|
15
|
+
self.query.select = options.delete(:select)
|
16
|
+
self.query.conditions = options.delete(:conditions) || []
|
17
|
+
self.query.uses = options.delete(:uses) || []
|
18
|
+
self.query.tail = options.delete(:tail)
|
19
|
+
self.query.truncate = options.delete(:truncate)
|
20
|
+
self.query.reverse = options.delete(:reverse)
|
21
|
+
self.query.unique = options.delete(:unique)
|
22
|
+
self.query.sanitize = options.delete(:sanitize)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Assigns the table for the query being constructed
|
26
|
+
#
|
27
|
+
# @param [String] table The name of the table.
|
28
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the table assigned.
|
29
|
+
def table(table)
|
30
|
+
self.query.table = table
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Assigns the limit for the query being constructed
|
35
|
+
#
|
36
|
+
# @param [Object] limit The limit for the query.
|
37
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the limit assigned.
|
38
|
+
#
|
39
|
+
# @example The limit may be passed as either a string or fixnum:
|
40
|
+
# base = Builder.new.limit(5)
|
41
|
+
# base = Builder.new.limit('5')
|
42
|
+
def limit(limit)
|
43
|
+
self.query.limit = limit
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
# Assigns the offset for the query being constructed
|
48
|
+
#
|
49
|
+
# @param [Object] offset The offset for the query.
|
50
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the offset assigned.
|
51
|
+
#
|
52
|
+
# @example The offset may be passed as either a string or fixnum:
|
53
|
+
# base = Builder.new.offset(5)
|
54
|
+
# base = Builder.new.offset('5')
|
55
|
+
def offset(offset)
|
56
|
+
self.query.offset = offset
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Assigns the columns to select for the query being constructed
|
61
|
+
#
|
62
|
+
# @param [Object] select The select arguments for the query.
|
63
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the select arguments assigned.
|
64
|
+
#
|
65
|
+
# @example The select may be passed as either a string or an array:
|
66
|
+
# base = Builder.new.select('name')
|
67
|
+
# base = Builder.new.select('name, age')
|
68
|
+
# base = Builder.new.select(['name', 'age'])
|
69
|
+
def select(select)
|
70
|
+
self.query.select = select
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Assigns additional datatable sources for use with the query being constructed
|
75
|
+
#
|
76
|
+
# @param [String] source The url of the data source.
|
77
|
+
# @param [String] as The name the data source will be referenced as in queries.
|
78
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the data source arguments assigned.
|
79
|
+
#
|
80
|
+
# @example The select may be passed as either a string or an array:
|
81
|
+
# base = Builder.new.use('http://anothersource/sometable.xml', 'sometable')
|
82
|
+
# base.to_s
|
83
|
+
# # => "use http://anothersource/sometable.xml as sometable; select * from tablename"
|
84
|
+
def use(source, as)
|
85
|
+
self.query.uses << Source.new(source, as)
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Assigns additional datatable sources for use with the query being constructed.
|
90
|
+
# Conditions are combined. Hashes assume equivalency when generating queries, except when passed a
|
91
|
+
# {Builder} instance in which case a sub-select is assumed using an 'in'.
|
92
|
+
#
|
93
|
+
# @param [Objct] conditions The conditions of the query.
|
94
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the data conditions assigned.
|
95
|
+
#
|
96
|
+
# @example The conditions may be passed as either a string, array or a hash:
|
97
|
+
# base = Builder.new.conditions("sub_genre = 'bebop'")
|
98
|
+
# base.to_s
|
99
|
+
# # => "select * from tablename where sub_genre = 'bebop'"
|
100
|
+
#
|
101
|
+
# base.conditions("genre = 'jazz'").conditions("sub_genre = 'bebop'")
|
102
|
+
# base.to_s
|
103
|
+
# # => "select * from tablename where genre = 'jazz' and sub_genre = 'bebop'"
|
104
|
+
#
|
105
|
+
# base.conditions(["genre = 'jazz'", "sub_genre = 'bebop'"])
|
106
|
+
# # => "select * from tablename where genre = 'jazz' and sub_genre = 'bebop'"
|
107
|
+
#
|
108
|
+
# # conditions are also aliased as 'where'
|
109
|
+
# base.where("genre = 'jazz'")
|
110
|
+
#
|
111
|
+
# guid_query = Builder.new.table('users').select('guid').where("role = 'admin'")
|
112
|
+
# base = Builder.new.table('actions).where(:guid => guid_query)
|
113
|
+
# base.to_s
|
114
|
+
# => "select * from actions where guid in (select guid from users where role = 'admin')"
|
115
|
+
def conditions(conditions)
|
116
|
+
if conditions.kind_of?(String)
|
117
|
+
self.query.conditions << conditions
|
118
|
+
elsif conditions.kind_of?(Array)
|
119
|
+
self.query.conditions += conditions
|
120
|
+
elsif conditions.kind_of?(Hash)
|
121
|
+
conditions.each do |key, value|
|
122
|
+
if value.kind_of?(YqlQuery::Builder)
|
123
|
+
self.query.conditions << "#{key} in (#{value})"
|
124
|
+
else
|
125
|
+
self.query.conditions << "#{key} = '#{value}'"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
self
|
130
|
+
end
|
131
|
+
alias :where :conditions
|
132
|
+
|
133
|
+
# Assigns the field to be sorted for the query being constructed
|
134
|
+
#
|
135
|
+
# @param [Object] sort The column to sort for the query.
|
136
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the sort assigned.
|
137
|
+
#
|
138
|
+
# @example
|
139
|
+
# base = Builder.new.sort('name)
|
140
|
+
def sort(sort)
|
141
|
+
self.query.sort = sort
|
142
|
+
self.query.sort_descending = false
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
# Assigns the field to be sorted for the query being constructed
|
147
|
+
#
|
148
|
+
# @param [Object] sort The column to sort descending for the query.
|
149
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the sort assigned.
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# base = Builder.new.sort('name)
|
153
|
+
def sort_descending(sort)
|
154
|
+
self.query.sort = sort
|
155
|
+
self.query.sort_descending = true
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Assigns the tail argument for the query being constructed
|
160
|
+
#
|
161
|
+
# @param [Object] tail The tail argument for the query.
|
162
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the tail filter assigned.
|
163
|
+
#
|
164
|
+
# @example The tail argument may be passed as either a string or fixnum:
|
165
|
+
# base = Builder.new.tail(5)
|
166
|
+
# base = Builder.new.tail('5')
|
167
|
+
def tail(tail)
|
168
|
+
self.query.tail = tail
|
169
|
+
self
|
170
|
+
end
|
171
|
+
|
172
|
+
# Assigns the truncate argument for the query being constructed
|
173
|
+
#
|
174
|
+
# @param [Object] truncate The truncate argument for the query.
|
175
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the truncate filter assigned.
|
176
|
+
#
|
177
|
+
# @example The truncate argument may be passed as either a string or fixnum:
|
178
|
+
# base = Builder.new.truncate(5)
|
179
|
+
# base = Builder.new.truncate('5')
|
180
|
+
# base.to_s
|
181
|
+
# # => "select * from tablename | truncate(5)"
|
182
|
+
def truncate(truncate)
|
183
|
+
self.query.truncate = truncate
|
184
|
+
self
|
185
|
+
end
|
186
|
+
|
187
|
+
# Adds the reverse filter to the query being constructed
|
188
|
+
#
|
189
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the offset assigned.
|
190
|
+
#
|
191
|
+
# @example
|
192
|
+
# base = Builder.new.reverse
|
193
|
+
# base.to_s
|
194
|
+
# # => "select * from tablename | reverse"
|
195
|
+
def reverse
|
196
|
+
self.query.reverse = true
|
197
|
+
self
|
198
|
+
end
|
199
|
+
|
200
|
+
# Assigns the unique argument for the query being constructed
|
201
|
+
#
|
202
|
+
# @param [Object] truncate The unique argument for the query.
|
203
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the unique filter assigned.
|
204
|
+
#
|
205
|
+
# @example
|
206
|
+
# base = Builder.new.unique('genre')
|
207
|
+
# base.to_s
|
208
|
+
# # => "select * from tablename | unique(field='genre')"
|
209
|
+
def unique(unique)
|
210
|
+
self.query.unique = unique
|
211
|
+
self
|
212
|
+
end
|
213
|
+
|
214
|
+
# Assigns the sanitize argument for the query being constructed
|
215
|
+
#
|
216
|
+
# @param [Object] sanitize The sanitize argument for the query.
|
217
|
+
# @return [YqlQuery::Builder] YqlQuery::Builder instance reflecting the sanitize filter assigned.
|
218
|
+
#
|
219
|
+
# @example Sanitize accepts either a string representing the column name or a boolean 'true' to sanitize all
|
220
|
+
# base = Builder.new.sanitize('genre')
|
221
|
+
# base.to_s
|
222
|
+
# # => "select * from tablename | sanitize(field='genre')"
|
223
|
+
#
|
224
|
+
# base = Builder.new.sanitize(true)
|
225
|
+
# base.to_s
|
226
|
+
# # => "select * from tablename | sanitize()"
|
227
|
+
def sanitize(sanitize=true)
|
228
|
+
self.query.sanitize = sanitize
|
229
|
+
self
|
230
|
+
end
|
231
|
+
|
232
|
+
# Returns the generated YQL query based on the arguments provided
|
233
|
+
def to_s
|
234
|
+
self.query.to_s
|
235
|
+
end
|
236
|
+
alias :to_query :to_s
|
237
|
+
|
238
|
+
# Resets all arguments to the query
|
239
|
+
def reset
|
240
|
+
self.query = Query.new
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module YqlQuery
|
2
|
+
|
3
|
+
# The object underlying {Builder} which stores and generates the query.
|
4
|
+
class Query
|
5
|
+
attr_accessor :table, :limit, :offset, :select, :uses, :conditions
|
6
|
+
attr_accessor :sort, :tail, :truncate, :reverse, :unique, :sanitize
|
7
|
+
attr_accessor :sort_descending
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.conditions = []
|
11
|
+
self.uses = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# generates a query based on it's attributes
|
15
|
+
def to_s
|
16
|
+
[use_statement, select_statement, conditions_statement, limit_offset_statement, filter_statement].join(' ').squeeze(' ').strip
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def use_statement
|
21
|
+
@uses.map { |use| "use #{use.source} as #{use.as};" }.join(' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
def select_statement
|
25
|
+
stmt = 'select '
|
26
|
+
stmt << case @select
|
27
|
+
when String
|
28
|
+
@select
|
29
|
+
when Array
|
30
|
+
@select.join(', ')
|
31
|
+
else
|
32
|
+
'*'
|
33
|
+
end
|
34
|
+
stmt << " from "
|
35
|
+
stmt << @table if @table
|
36
|
+
stmt
|
37
|
+
end
|
38
|
+
|
39
|
+
def conditions_statement
|
40
|
+
@conditions.uniq.any? ? "where #{@conditions.uniq.join(' and ')}" : ""
|
41
|
+
end
|
42
|
+
|
43
|
+
def limit_offset_statement
|
44
|
+
stmt = ''
|
45
|
+
stmt << "limit #{@limit}" if @limit
|
46
|
+
stmt << "offset #{@offset}" if @offset
|
47
|
+
stmt
|
48
|
+
end
|
49
|
+
|
50
|
+
def filter_statement
|
51
|
+
statements = []
|
52
|
+
if @sort && @sort_descending
|
53
|
+
statements << "sort(field='#{@sort}', descending='true')"
|
54
|
+
elsif @sort
|
55
|
+
statements << "sort(field='#{@sort}')"
|
56
|
+
end
|
57
|
+
|
58
|
+
statements << "tail(count=#{@tail})" if @tail
|
59
|
+
statements << "truncate(count=#{@truncate})" if @truncate
|
60
|
+
statements << "reverse()" if @reverse
|
61
|
+
statements << "unique(field='#{@unique}')" if @unique
|
62
|
+
if @sanitize && @sanitize == true
|
63
|
+
statements << "sanitize()"
|
64
|
+
elsif @sanitize
|
65
|
+
statements << "sanitize(field='#{@sanitize}')"
|
66
|
+
end
|
67
|
+
statements.any? ? "| #{statements.join(' | ')}" : ''
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module YqlQuery
|
2
|
+
|
3
|
+
# A class used by {Builder} to store additional data sources.
|
4
|
+
class Source
|
5
|
+
attr_accessor :source, :as
|
6
|
+
|
7
|
+
def initialize(source, as)
|
8
|
+
@source = source
|
9
|
+
@as = as
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sources are equal if both their 'source' and 'as' attributes are equivalent.
|
13
|
+
def ==(b)
|
14
|
+
self.source == b.source && self.as == b.as
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,330 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe YqlQuery::Builder do
|
4
|
+
before(:each) do
|
5
|
+
@builder = YqlQuery::Builder.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#table()" do
|
9
|
+
it "should set the table" do
|
10
|
+
@builder.table('music.artists')
|
11
|
+
@builder.query.table.should == 'music.artists'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return the builder" do
|
15
|
+
@builder.table('music.artists').should be_kind_of(YqlQuery::Builder)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#limit()" do
|
20
|
+
it "should set the limit" do
|
21
|
+
@builder.limit(5)
|
22
|
+
@builder.query.limit.should == 5
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return the builder" do
|
26
|
+
@builder.limit(5).should be_kind_of(YqlQuery::Builder)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#offset()" do
|
31
|
+
it "should set the offset" do
|
32
|
+
@builder.offset(10)
|
33
|
+
@builder.query.offset.should == 10
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return the builder" do
|
37
|
+
@builder.offset(10).should be_kind_of(YqlQuery::Builder)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#select()" do
|
42
|
+
it "should set the select" do
|
43
|
+
@builder.select('name')
|
44
|
+
@builder.query.select.should == 'name'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should accept select as an array" do
|
48
|
+
@builder.select(['age', 'sex', 'language', 'other'])
|
49
|
+
@builder.query.select.should == ['age', 'sex', 'language', 'other']
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return the builder" do
|
53
|
+
@builder.select('name').should be_kind_of(YqlQuery::Builder)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#use()" do
|
58
|
+
it "should set the use" do
|
59
|
+
@builder.use('http://namedata.com', 'names')
|
60
|
+
@builder.query.uses.include?(YqlQuery::Source.new('http://namedata.com', 'names')).should be_true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should return the builder" do
|
64
|
+
@builder.use('http://namedata.com', 'names').should be_kind_of(YqlQuery::Builder)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#conditions()" do
|
69
|
+
it "should set the conditions" do
|
70
|
+
@builder.conditions("name = 'fred'")
|
71
|
+
@builder.query.conditions.include?("name = 'fred'").should be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return the builder" do
|
75
|
+
@builder.conditions("name = 'fred'").should be_kind_of(YqlQuery::Builder)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be aliased as #where()" do
|
79
|
+
@builder.where("name = 'jeff'")
|
80
|
+
@builder.query.conditions.include?("name = 'jeff'").should be_true
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should store all conditions" do
|
84
|
+
@builder.conditions("name = 'fred'")
|
85
|
+
@builder.conditions("age = 22")
|
86
|
+
@builder.query.conditions.include?("name = 'fred'").should be_true
|
87
|
+
@builder.query.conditions.include?("age = 22").should be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should accept conditions as an array" do
|
91
|
+
@builder.conditions(["name = 'greg'", "age = 34"])
|
92
|
+
@builder.query.conditions.include?("name = 'greg'").should be_true
|
93
|
+
@builder.query.conditions.include?("age = 34").should be_true
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should accept conditions as a hash" do
|
97
|
+
@builder.conditions({ :genre => 'jazz', :type => 'bebop'})
|
98
|
+
@builder.query.conditions.include?("genre = 'jazz'").should be_true
|
99
|
+
@builder.query.conditions.include?("type = 'bebop'").should be_true
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should accept conditions as a hash when passed another query" do
|
103
|
+
@builder.conditions({ :artist_id => YqlQuery::Builder.new.table('music.amg.artists').select('artist_id')})
|
104
|
+
@builder.query.conditions.include?("artist_id in (select artist_id from music.amg.artists)").should be_true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#sort()" do
|
109
|
+
it "should set the sort" do
|
110
|
+
@builder.sort('nickname')
|
111
|
+
@builder.query.sort.should == 'nickname'
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should return the builder" do
|
115
|
+
@builder.sort('nickname').should be_kind_of(YqlQuery::Builder)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#sort_descending()" do
|
120
|
+
it "should set the sort" do
|
121
|
+
@builder.sort_descending('nickname')
|
122
|
+
@builder.query.sort.should == 'nickname'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should return the builder" do
|
126
|
+
@builder.sort('nickname').should be_kind_of(YqlQuery::Builder)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "#tail()" do
|
131
|
+
it "should set the tail" do
|
132
|
+
@builder.tail(3)
|
133
|
+
@builder.query.tail.should == 3
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should return the builder" do
|
137
|
+
@builder.tail(3).should be_kind_of(YqlQuery::Builder)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#truncate()" do
|
142
|
+
it "should set the truncate option" do
|
143
|
+
@builder.truncate(3)
|
144
|
+
@builder.query.truncate.should == 3
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should return the builder" do
|
148
|
+
@builder.truncate(3).should be_kind_of(YqlQuery::Builder)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "#reverse" do
|
153
|
+
it "should set the reverse option" do
|
154
|
+
@builder.reverse
|
155
|
+
@builder.query.reverse.should be_true
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return the builder" do
|
159
|
+
@builder.reverse.should be_kind_of(YqlQuery::Builder)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#unique()" do
|
164
|
+
it "should set the unique option" do
|
165
|
+
@builder.unique("Rating.AverageRating")
|
166
|
+
@builder.query.unique.should == "Rating.AverageRating"
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should return the builder" do
|
170
|
+
@builder.unique("Rating.AverageRating").should be_kind_of(YqlQuery::Builder)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "#sanitize()" do
|
175
|
+
it "should set the unique option" do
|
176
|
+
@builder.sanitize("Rating.Description")
|
177
|
+
@builder.query.sanitize.should == "Rating.Description"
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should default sanitize to true" do
|
181
|
+
@builder.sanitize
|
182
|
+
@builder.query.sanitize.should be_true
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should return the builder" do
|
186
|
+
@builder.sanitize("Rating.Description").should be_kind_of(YqlQuery::Builder)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
context "chaining" do
|
191
|
+
it "should combine query options" do
|
192
|
+
@builder.table('music.artists').limit(5).conditions("name = 'Jose James'")
|
193
|
+
@builder.query.limit.should == 5
|
194
|
+
@builder.query.table.should == 'music.artists'
|
195
|
+
@builder.query.conditions.include?("name = 'Jose James'").should be_true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe "#to_s" do
|
200
|
+
it "should return the generated query" do
|
201
|
+
@builder.table('music.artists').limit(5).conditions("name = 'Jose James'")
|
202
|
+
@builder.to_s.should == "select * from music.artists where name = 'Jose James' limit 5"
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should be aliased as to_query" do
|
206
|
+
@builder.table('music.artists').limit(5).conditions("name = 'Jose James'")
|
207
|
+
@builder.to_s.should == @builder.to_query
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "#reset" do
|
212
|
+
it "should reset all query conditions" do
|
213
|
+
@builder.table('Rating')
|
214
|
+
@builder.limit(5)
|
215
|
+
@builder.offset(5)
|
216
|
+
@builder.select('Rating.Description, Rating.SomethingElse')
|
217
|
+
@builder.use('http://namedata.com', 'names')
|
218
|
+
@builder.conditions("name = 'fred'")
|
219
|
+
@builder.sort('nickname')
|
220
|
+
@builder.tail(5)
|
221
|
+
@builder.truncate(3)
|
222
|
+
@builder.reverse
|
223
|
+
@builder.unique("Rating.AverageRating")
|
224
|
+
@builder.sanitize("Rating.Description")
|
225
|
+
|
226
|
+
@builder.reset
|
227
|
+
|
228
|
+
@builder.query.table.should be_nil
|
229
|
+
@builder.query.limit.should be_nil
|
230
|
+
@builder.query.offset.should be_nil
|
231
|
+
@builder.query.select.should be_nil
|
232
|
+
@builder.query.limit.should be_nil
|
233
|
+
@builder.query.uses.should be_empty
|
234
|
+
@builder.query.conditions.should be_empty
|
235
|
+
@builder.query.sort.should be_nil
|
236
|
+
@builder.query.tail.should be_nil
|
237
|
+
@builder.query.truncate.should be_nil
|
238
|
+
@builder.query.reverse.should be_nil
|
239
|
+
@builder.query.unique.should be_nil
|
240
|
+
@builder.query.sanitize.should be_nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context "generating queries" do
|
245
|
+
before(:each) do
|
246
|
+
@builder.table('music.artists')
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should generate ther right query when given a different data source to use" do
|
250
|
+
@builder.conditions("name = 'Jose James'").use('http://musicbrainz.org/data.xml', 'musicBrainzArtists').table('musicBrainzArtists')
|
251
|
+
@builder.to_s.should == "use http://musicbrainz.org/data.xml as musicBrainzArtists; select * from musicBrainzArtists where name = 'Jose James'"
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should generate ther right query when given multiple data sources to use" do
|
255
|
+
@builder.conditions("name = 'Jose James'").use('http://musicbrainz.org/data.xml', 'musicBrainzArtists').use('http://musicbrainz.org/albums.xml', 'musicBrainzAlbums').table('musicBrainzArtists')
|
256
|
+
@builder.to_s.should == "use http://musicbrainz.org/data.xml as musicBrainzArtists; use http://musicbrainz.org/albums.xml as musicBrainzAlbums; select * from musicBrainzArtists where name = 'Jose James'"
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should generate the right query when given a single condition" do
|
260
|
+
@builder.conditions("name = 'Jose James'")
|
261
|
+
@builder.to_s.should == "select * from music.artists where name = 'Jose James'"
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should generate the right query when given multiple conditions" do
|
265
|
+
@builder.conditions("name = 'Jose James'").conditions("genre = 'Jazz'")
|
266
|
+
@builder.to_s.should == "select * from music.artists where name = 'Jose James' and genre = 'Jazz'"
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should generate the right query when given a limit" do
|
270
|
+
@builder.conditions("name = 'John'").limit(5)
|
271
|
+
@builder.to_s.should == "select * from music.artists where name = 'John' limit 5"
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should generate the right query when given an offset" do
|
275
|
+
@builder.conditions("name = 'John'").offset(15)
|
276
|
+
@builder.to_s.should == "select * from music.artists where name = 'John' offset 15"
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should generate the right query when given a select" do
|
280
|
+
@builder.conditions("name = 'John'").select('Title, First Name, Email')
|
281
|
+
@builder.to_s.should == "select Title, First Name, Email from music.artists where name = 'John'"
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should generate the right query when given a select as an array" do
|
285
|
+
@builder.conditions("name = 'John'").select(['age', 'sex', 'language', 'other'])
|
286
|
+
@builder.to_s.should == "select age, sex, language, other from music.artists where name = 'John'"
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should generate the right query when given a sort" do
|
290
|
+
@builder.sort('popularity')
|
291
|
+
@builder.to_s.should == "select * from music.artists | sort(field='popularity')"
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should generate the right query when given a sort descending" do
|
295
|
+
@builder.sort_descending('popularity')
|
296
|
+
@builder.to_s.should == "select * from music.artists | sort(field='popularity', descending='true')"
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should generate the right query when given a tail" do
|
300
|
+
@builder.tail(5)
|
301
|
+
@builder.to_s.should == "select * from music.artists | tail(count=5)"
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should generate the right query when given a truncate" do
|
305
|
+
@builder.truncate(5)
|
306
|
+
@builder.to_s.should == "select * from music.artists | truncate(count=5)"
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should generate the right query when given a reverse" do
|
310
|
+
@builder.reverse
|
311
|
+
@builder.to_s.should == "select * from music.artists | reverse()"
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should generate the right query when given a unique" do
|
315
|
+
@builder.unique('art')
|
316
|
+
@builder.to_s.should == "select * from music.artists | unique(field='art')"
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should generate the right query when given a sanitize" do
|
320
|
+
@builder.sanitize('art')
|
321
|
+
@builder.to_s.should == "select * from music.artists | sanitize(field='art')"
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should generate the right query when given a sanitize all fields" do
|
325
|
+
@builder.sanitize
|
326
|
+
@builder.to_s.should == "select * from music.artists | sanitize()"
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe YqlQuery::Source do
|
4
|
+
before(:each) do
|
5
|
+
@use_case = YqlQuery::Source.new('http://geodata.org/table.xml', 'geodata')
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be equivalent if both the source and as are equal" do
|
9
|
+
@use_case.should == YqlQuery::Source.new('http://geodata.org/table.xml', 'geodata')
|
10
|
+
end
|
11
|
+
end
|
data/yql-query.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "yql_query/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "yql-query"
|
7
|
+
s.version = YqlQuery::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Steve Agalloco"]
|
10
|
+
s.email = ["steve.agalloco@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/spagalloco/yql-query"
|
12
|
+
s.summary = %q{A YQL Query generator}
|
13
|
+
s.description = %q{A YQL Query generator}
|
14
|
+
|
15
|
+
s.rubyforge_project = "yql-query"
|
16
|
+
|
17
|
+
s.add_runtime_dependency('hashie', '~> 1.0.0')
|
18
|
+
|
19
|
+
s.add_development_dependency('bundler', '~> 1.0')
|
20
|
+
s.add_development_dependency('rake', '~> 0.8')
|
21
|
+
s.add_development_dependency('rspec', '~> 2.5.0')
|
22
|
+
s.add_development_dependency('yard', '~> 0.6')
|
23
|
+
s.add_development_dependency('maruku', '~> 0.6')
|
24
|
+
s.add_development_dependency('simplecov', '~> 0.3')
|
25
|
+
|
26
|
+
s.files = `git ls-files`.split("\n")
|
27
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yql-query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Steve Agalloco
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-02-22 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hashie
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.0.0
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "1.0"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rake
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0.8"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 2.5.0
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: yard
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0.6"
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: maruku
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0.6"
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: simplecov
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0.3"
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id007
|
93
|
+
description: A YQL Query generator
|
94
|
+
email:
|
95
|
+
- steve.agalloco@gmail.com
|
96
|
+
executables: []
|
97
|
+
|
98
|
+
extensions: []
|
99
|
+
|
100
|
+
extra_rdoc_files: []
|
101
|
+
|
102
|
+
files:
|
103
|
+
- .gitignore
|
104
|
+
- .rspec
|
105
|
+
- Gemfile
|
106
|
+
- License.md
|
107
|
+
- Rakefile
|
108
|
+
- Readme.md
|
109
|
+
- lib/yql-query.rb
|
110
|
+
- lib/yql_query.rb
|
111
|
+
- lib/yql_query/builder.rb
|
112
|
+
- lib/yql_query/query.rb
|
113
|
+
- lib/yql_query/source.rb
|
114
|
+
- lib/yql_query/version.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- spec/yql_query_builder_spec.rb
|
117
|
+
- spec/yql_query_source_spec.rb
|
118
|
+
- yql-query.gemspec
|
119
|
+
has_rdoc: true
|
120
|
+
homepage: https://github.com/spagalloco/yql-query
|
121
|
+
licenses: []
|
122
|
+
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: "0"
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: "0"
|
140
|
+
requirements: []
|
141
|
+
|
142
|
+
rubyforge_project: yql-query
|
143
|
+
rubygems_version: 1.5.0
|
144
|
+
signing_key:
|
145
|
+
specification_version: 3
|
146
|
+
summary: A YQL Query generator
|
147
|
+
test_files:
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/yql_query_builder_spec.rb
|
150
|
+
- spec/yql_query_source_spec.rb
|