yql 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +7 -0
- data/README.rdoc +105 -0
- data/lib/yql.rb +12 -0
- data/lib/yql/client.rb +61 -0
- data/lib/yql/error.rb +19 -0
- data/lib/yql/query_builder.rb +186 -0
- metadata +69 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
==DESCRIPTION
|
2
|
+
|
3
|
+
A basic Ruby Wrapper for interacting programatically with YQL API.
|
4
|
+
|
5
|
+
|
6
|
+
==TODO
|
7
|
+
|
8
|
+
1. Add Unit Tests
|
9
|
+
2. Add oauth
|
10
|
+
3. Add table creation
|
11
|
+
4. Add update / insert / delete operations
|
12
|
+
|
13
|
+
==INSTALLATION
|
14
|
+
|
15
|
+
sudo gem source --add http://rubygems.org
|
16
|
+
|
17
|
+
sudo gem install yql
|
18
|
+
|
19
|
+
|
20
|
+
==USAGE
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
|
24
|
+
require 'yql'
|
25
|
+
|
26
|
+
===Building Query
|
27
|
+
|
28
|
+
|
29
|
+
====Finders
|
30
|
+
|
31
|
+
yql = Yql::Client.new
|
32
|
+
|
33
|
+
query = Yql::QueryBuilder.new 'yelp.review.search'
|
34
|
+
|
35
|
+
query.to_s #=> "select * from yelp.review.search"
|
36
|
+
|
37
|
+
query.find #=> "select * from yelp.review.search limit 1"
|
38
|
+
|
39
|
+
query.limit = 4
|
40
|
+
|
41
|
+
query.to_s #=> "select * from yelp.review.search limit 4"
|
42
|
+
|
43
|
+
query.find_all #=> "select * from yelp.review.search"
|
44
|
+
|
45
|
+
|
46
|
+
====Conditions
|
47
|
+
|
48
|
+
query.conditions = "term like '%pizza%'"
|
49
|
+
|
50
|
+
query.to_s #=> "select * from yelp.review.search where term='%pizza%'"
|
51
|
+
|
52
|
+
query.conditions = {:term => 'pizza'}
|
53
|
+
|
54
|
+
query.to_s #=> "select * from yelp.review.search where term = 'pizza'"
|
55
|
+
|
56
|
+
query.conditions = {:term => 'pizza', :location => 'london', 'ywsid' => '6L0Lc-yn1OKMkCKeXLD4lg'}
|
57
|
+
|
58
|
+
query.to_s #=> "select * from yelp.review.search where term='pizza' and location='london' and ywsid= '6L0Lc-yn1OKMkCKeXLD4lg'"
|
59
|
+
|
60
|
+
query.select = 'user_photo_url, state'
|
61
|
+
|
62
|
+
yql.query = query
|
63
|
+
response = yql.get
|
64
|
+
|
65
|
+
yql.format = 'json'
|
66
|
+
response = yql.get #=> Yql::Response object
|
67
|
+
|
68
|
+
|
69
|
+
===Piped Filters
|
70
|
+
|
71
|
+
query.unique = 'name'
|
72
|
+
|
73
|
+
query.to_s #=> "select title, Rating, LastReviewIntro from yelp.review.search where ywsid='6L0Lc-yn1OKMkCKeXLD4lg' and term='pizza' and location='london' | unique(field='name')"
|
74
|
+
|
75
|
+
query.tail = 4
|
76
|
+
|
77
|
+
query.to_s #=> "select title, Rating, LastReviewIntro from yelp.review.search where ywsid='6L0Lc-yn1OKMkCKeXLD4lg' and term='pizza' and location='london' | unique(field='name') | tail(count=4)"
|
78
|
+
|
79
|
+
query.reorder_pipe_command :from => 1, :to => 0
|
80
|
+
|
81
|
+
query.to_s #=> "select title, Rating, LastReviewIntro from yelp.review.search where ywsid='6L0Lc-yn1OKMkCKeXLD4lg' and term='pizza' and location='london' | tail(count=4) | unique(field='name')"
|
82
|
+
|
83
|
+
yql.format = 'json'
|
84
|
+
response = yql.get #=> Yql::Response object
|
85
|
+
|
86
|
+
|
87
|
+
====Pagination
|
88
|
+
|
89
|
+
query.per_page = 10
|
90
|
+
query.current_page = 1
|
91
|
+
|
92
|
+
yql.query = query
|
93
|
+
response = yql.get #=> Yql::Response object
|
94
|
+
|
95
|
+
|
96
|
+
===Describe and show tables
|
97
|
+
|
98
|
+
query = Yql::QueryBuilder.describe_table('yelp.review.search')
|
99
|
+
|
100
|
+
query = Yql::QueryBuilder.show_tables
|
101
|
+
|
102
|
+
yql.query = query
|
103
|
+
response = yql.get #=> Yql::Response object
|
104
|
+
|
105
|
+
response.show
|
data/lib/yql.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'CGI'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
require 'rexml/document'
|
9
|
+
require 'yql/error.rb'
|
10
|
+
require 'yql/client.rb'
|
11
|
+
require 'yql/query_builder.rb'
|
12
|
+
require 'yql/response.rb'
|
data/lib/yql/client.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Yql
|
2
|
+
|
3
|
+
class Client
|
4
|
+
|
5
|
+
BASE_URL = 'query.yahooapis.com'
|
6
|
+
VERSION = 'v1'
|
7
|
+
URL_SUFFIX = 'public/yql'
|
8
|
+
YQL_ENV = 'http://datatables.org/alltables.env'
|
9
|
+
|
10
|
+
attr_accessor :query, :diagnostics, :format
|
11
|
+
|
12
|
+
def initialize(args={})
|
13
|
+
@diagnostics = args[:diagnostics] #true or false
|
14
|
+
@version = args[:version]
|
15
|
+
@query = args[:query]
|
16
|
+
@format = args[:format] || 'xml'
|
17
|
+
end
|
18
|
+
|
19
|
+
def query
|
20
|
+
@query.kind_of?(Yql::QueryBuilder) ? @query.to_s : @query
|
21
|
+
end
|
22
|
+
|
23
|
+
def version
|
24
|
+
@version ||= VERSION
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_url
|
28
|
+
"#{version}/#{URL_SUFFIX}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def get
|
32
|
+
if query.nil?
|
33
|
+
raise Yql::IncompleteRequestParameter, "Query not specified"
|
34
|
+
end
|
35
|
+
http = Net::HTTP.new(BASE_URL, Net::HTTP.https_default_port)
|
36
|
+
http.use_ssl = true
|
37
|
+
path = "/#{version}/#{URL_SUFFIX}"
|
38
|
+
Yql::Response.new(http.post(path, parameters), format)
|
39
|
+
end
|
40
|
+
|
41
|
+
def parameters
|
42
|
+
url_parameters = "q=#{CGI.escape(query)}&env=#{YQL_ENV}"
|
43
|
+
url_parameters = add_format(url_parameters)
|
44
|
+
add_diagnostics(url_parameters)
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_format(existing_parameters)
|
48
|
+
return unless existing_parameters
|
49
|
+
return existing_parameters unless format
|
50
|
+
return existing_parameters + "&format=#{format}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_diagnostics(existing_parameters)
|
54
|
+
return unless existing_parameters
|
55
|
+
return existing_parameters unless diagnostics
|
56
|
+
return existing_parameters + "&diagnostics=true"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/lib/yql/error.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
module Yql
|
2
|
+
|
3
|
+
class QueryBuilder
|
4
|
+
|
5
|
+
attr_accessor :table, :conditions, :limit, :truncate, :sanitize_field, :select,
|
6
|
+
:sort_field, :current_pipe_command_types, :sort_descending,
|
7
|
+
:per_page, :current_page
|
8
|
+
|
9
|
+
def initialize(table, args = {})
|
10
|
+
@select = args[:select]
|
11
|
+
@table = table
|
12
|
+
@use_statement = args[:use]
|
13
|
+
@conditions = args[:conditions]
|
14
|
+
@limit = args[:limit]
|
15
|
+
@tail = args[:tail]
|
16
|
+
@reverse = args[:reverse]
|
17
|
+
@unique = args[:unique]
|
18
|
+
@sanitize = args[:sanitize]
|
19
|
+
@sort_descending = args[:sort_descending]
|
20
|
+
@sanitize_field = args[:sanitize_field]
|
21
|
+
@sort_field = args[:sort_field]
|
22
|
+
@current_pipe_command_types = []
|
23
|
+
@per_page = args[:per_page]
|
24
|
+
end
|
25
|
+
|
26
|
+
def find
|
27
|
+
self.limit = 1
|
28
|
+
"#{construct_query}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_all
|
32
|
+
self.limit = nil
|
33
|
+
construct_query
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.show_tables
|
37
|
+
"show tables;"
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.describe_table(table)
|
41
|
+
"desc #{table};"
|
42
|
+
end
|
43
|
+
|
44
|
+
def describe_table
|
45
|
+
Yql::QueryBuilder.describle_table(table)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
construct_query
|
50
|
+
end
|
51
|
+
|
52
|
+
def limit
|
53
|
+
return unless @limit
|
54
|
+
"limit #{@limit}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Conditions can either be provided as hash or plane string
|
58
|
+
def conditions
|
59
|
+
if @conditions.kind_of?(String)
|
60
|
+
cond = @conditions
|
61
|
+
elsif @conditions.kind_of?(Hash)
|
62
|
+
cond = @conditions.collect do |k,v|
|
63
|
+
val = v.kind_of?(String) ? "'#{v}'" : v
|
64
|
+
"#{k.to_s}=#{val}"
|
65
|
+
end
|
66
|
+
cond = cond.join(' and ')
|
67
|
+
else
|
68
|
+
return
|
69
|
+
end
|
70
|
+
return "where #{cond}"
|
71
|
+
end
|
72
|
+
|
73
|
+
%w{sort tail truncate reverse unique sanitize}.each do |method|
|
74
|
+
self.send(:define_method, "#{method}=") do |param|
|
75
|
+
instance_variable_set("@#{method}", param)
|
76
|
+
current_pipe_command_types << method unless current_pipe_command_types.include?(method)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Cption can be piped
|
81
|
+
# Sorts the result set according to the specified field (column) in the result set.
|
82
|
+
def sort
|
83
|
+
return unless @sort_field
|
84
|
+
return "sort(field='#{@sort_field}')" unless @sort_descending
|
85
|
+
return "sort(field='#{@sort_field}', descending='true')"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Cption can be piped
|
89
|
+
# Gets the last count items
|
90
|
+
def tail
|
91
|
+
return unless @tail
|
92
|
+
"tail(count=#{@tail})"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Cption can be piped
|
96
|
+
# Gets the first count items (rows)
|
97
|
+
def truncate
|
98
|
+
return unless @truncate
|
99
|
+
"truncate(count=#{@truncate})"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Cption can be piped
|
103
|
+
# Reverses the order of the rows
|
104
|
+
def reverse
|
105
|
+
return unless @reverse
|
106
|
+
"reverse()"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Cption can be piped
|
110
|
+
# Removes items (rows) with duplicate values in the specified field (column)
|
111
|
+
def unique
|
112
|
+
return unless @unique
|
113
|
+
"unique(field='#{@unique}')"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Cption can be piped
|
117
|
+
# Sanitizes the output for HTML-safe rendering. To sanitize all returned fields, omit the field parameter.
|
118
|
+
def sanitize
|
119
|
+
return unless @sanitize
|
120
|
+
return "sanitize()" unless @sanitize_field
|
121
|
+
"sanitize(field='#{@sanitize_field}')"
|
122
|
+
end
|
123
|
+
|
124
|
+
# Its always advisable to order the pipe when there are more than one
|
125
|
+
# pipe commands available else unexpected results might get returned
|
126
|
+
# reorder_pipe_command {:from => 1, :to => 0}
|
127
|
+
# the values in the hash are the element numbers
|
128
|
+
#
|
129
|
+
def reorder_pipe_command(args)
|
130
|
+
return if current_pipe_command_types.empty?
|
131
|
+
if args[:from].nil? or args[:to].nil?
|
132
|
+
raise Yql::Error, "Not able to move pipe commands. Wrong element numbers. Please try again"
|
133
|
+
end
|
134
|
+
args.values.each do |element|
|
135
|
+
if element > current_pipe_command_types.size-1
|
136
|
+
raise Yql::Error, "Not able to move pipe commands. Wrong element numbers. Please try again"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
element_to_be_inserted_at = args[:from] < args[:to] ? args[:to]+1 : args[:to]
|
140
|
+
element_to_be_removed = args[:from] < args[:to] ? args[:from] : args[:from]+1
|
141
|
+
current_pipe_command_types.insert(element_to_be_inserted_at, current_pipe_command_types.at(args[:from]))
|
142
|
+
current_pipe_command_types.delete_at(element_to_be_removed)
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# Remove a command that will be piped to the yql query
|
147
|
+
def remove_pipe_command(command)
|
148
|
+
current_pipe_command_types.delete(command)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def pipe_commands
|
154
|
+
return if current_pipe_command_types.empty?
|
155
|
+
'| ' + current_pipe_command_types.map{|c| self.send(c)}.join(' | ')
|
156
|
+
end
|
157
|
+
|
158
|
+
def build_select_query
|
159
|
+
[select_statement, conditions, limit, pipe_commands].compact.join(' ')
|
160
|
+
end
|
161
|
+
|
162
|
+
def construct_query
|
163
|
+
return build_select_query unless @use
|
164
|
+
return [@use, build_select_query].join('; ')
|
165
|
+
end
|
166
|
+
|
167
|
+
def select_statement
|
168
|
+
select = "select #{column_select} from #{table}"
|
169
|
+
return select unless per_page
|
170
|
+
with_pagination(select)
|
171
|
+
end
|
172
|
+
|
173
|
+
def with_pagination(select)
|
174
|
+
self.current_page ||= 1
|
175
|
+
offset = (current_page - 1) * per_page
|
176
|
+
last_record = current_page * per_page
|
177
|
+
"#{select}(#{offset},#{last_record})"
|
178
|
+
end
|
179
|
+
|
180
|
+
def column_select
|
181
|
+
@select ? @select : '*'
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nasir Jamal
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-20 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Yql is a ruby wrapper for Yahoo Query Language.
|
22
|
+
email: nas35_in@yahoo.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- History.txt
|
31
|
+
- Manifest.txt
|
32
|
+
- README.rdoc
|
33
|
+
- lib/yql.rb
|
34
|
+
- lib/yql/client.rb
|
35
|
+
- lib/yql/error.rb
|
36
|
+
- lib/yql/query_builder.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/nas/yql
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
segments:
|
51
|
+
- 1
|
52
|
+
- 8
|
53
|
+
version: "1.8"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.3.6
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Yql is a ruby wrapper for Yahoo Query Language.
|
68
|
+
test_files: []
|
69
|
+
|