yarel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/yarel.rb +26 -0
- data/lib/yarel/base.rb +36 -0
- data/lib/yarel/connection.rb +10 -0
- data/lib/yarel/extensions/object_extensions.rb +7 -0
- data/lib/yarel/table.rb +82 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/yarel/base_spec.rb +36 -0
- data/spec/yarel/yarel_spec.rb +91 -0
- metadata +114 -0
data/lib/yarel.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_model'
|
3
|
+
require 'net/http'
|
4
|
+
require 'cgi'
|
5
|
+
require 'active_support/core_ext/object/try'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
8
|
+
require 'active_support/core_ext/module/delegation'
|
9
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
10
|
+
|
11
|
+
module Yarel
|
12
|
+
extend ActiveSupport::Autoload
|
13
|
+
eager_autoload do
|
14
|
+
autoload :Table
|
15
|
+
autoload :Connection
|
16
|
+
# autoload :Base
|
17
|
+
end
|
18
|
+
|
19
|
+
def Yarel.const_missing(const_name)
|
20
|
+
yql_table_name = const_name.to_s.underscore.gsub("_", ".")
|
21
|
+
const_set const_name, Table.new(yql_table_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
require 'yarel/extensions/object_extensions'
|
25
|
+
|
26
|
+
Logger = ActiveSupport::BufferedLogger.new(File.open('yarel.log', 'w+')) unless Module.const_defined?("Logger")
|
data/lib/yarel/base.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Yarel
|
2
|
+
class Exception < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
module Base
|
6
|
+
attr_accessor :count
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def all
|
10
|
+
response = Connection.get(table.to_yql)
|
11
|
+
raise Exception.new(response["error"]["description"]) if response["error"]
|
12
|
+
[response["query"]["results"].first[1]].flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
[:sort, :order, :limit, :where, :from, :project, :select].each do |chainable_method|
|
16
|
+
class_eval <<-RUBY_EVAL, __FILE__, __LINE__
|
17
|
+
def #{chainable_method}(*args)
|
18
|
+
self.table = self.table.send(:#{chainable_method}, *args)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
RUBY_EVAL
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(klass)
|
26
|
+
klass.cattr_accessor :table
|
27
|
+
klass.table = Table.new(klass.name.underscore.gsub("/", "."), klass)
|
28
|
+
klass.instance_eval do
|
29
|
+
class << self
|
30
|
+
delegate :to_yql, :to => :table
|
31
|
+
end
|
32
|
+
end
|
33
|
+
klass.extend ClassMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Yarel
|
2
|
+
class Connection
|
3
|
+
END_POINT_URL = 'http://query.yahooapis.com/v1/public/yql'
|
4
|
+
|
5
|
+
def self.get(yql)
|
6
|
+
response = Net::HTTP.post_form(URI.parse(END_POINT_URL), { :q => yql, :format => :json })
|
7
|
+
ActiveSupport::JSON.decode response.body
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/yarel/table.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Yarel
|
2
|
+
class Exception < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class Table
|
6
|
+
attr_accessor :table_name, :projections, :conditions, :limit_to, :offset, :sort_columns
|
7
|
+
|
8
|
+
def initialize(table_name)
|
9
|
+
@table_name = table_name
|
10
|
+
@projections = "*"
|
11
|
+
@conditions = []
|
12
|
+
@limit_to = @offset = :default
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
yql = to_yql
|
17
|
+
Logger.info "Generated YQL: #{yql.inspect}\n"
|
18
|
+
response = Connection.get(yql)
|
19
|
+
raise Exception.new(response["error"]["description"]) if response["error"]
|
20
|
+
response["query"]["results"] ? [response["query"]["results"].first[1]].flatten : []
|
21
|
+
end
|
22
|
+
|
23
|
+
def from(table_name)
|
24
|
+
modify_clone { self.table_name = table_name }
|
25
|
+
end
|
26
|
+
|
27
|
+
def project(*field_names)
|
28
|
+
modify_clone { self.projections = field_names.join(", ") }
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method :select, :project
|
32
|
+
|
33
|
+
def where(condition)
|
34
|
+
new_condition =
|
35
|
+
case
|
36
|
+
when condition.kind_of?(Hash)
|
37
|
+
condition.map do |key, value|
|
38
|
+
condition_string = value.kind_of?(self.class) ? "#{key} in ( #{value.to_yql} )" : "#{key} = '#{value}'"
|
39
|
+
end
|
40
|
+
when condition.kind_of?(String)
|
41
|
+
condition
|
42
|
+
when condition.kind_of?(Array)
|
43
|
+
send :sprintf, condition[0].gsub("?", "'%s'"), *condition[1..-1]
|
44
|
+
end
|
45
|
+
|
46
|
+
modify_clone { self.conditions << new_condition }
|
47
|
+
end
|
48
|
+
|
49
|
+
def limit(*options)
|
50
|
+
lim = options[0]
|
51
|
+
off, lim = options[0..1] unless options.size == 1
|
52
|
+
modify_clone {
|
53
|
+
self.limit_to = lim.to_i if lim
|
54
|
+
self.offset = off.to_i if off
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def sort(*columns)
|
59
|
+
modify_clone { self.sort_columns = columns.try(:join, ", ") }
|
60
|
+
end
|
61
|
+
|
62
|
+
alias_method :order, :sort
|
63
|
+
|
64
|
+
def to_yql
|
65
|
+
yql = ["SELECT #{projections} FROM #{table_name}"]
|
66
|
+
yql << "WHERE #{conditions.join(' AND ')}" unless conditions.empty?
|
67
|
+
yql << "LIMIT #{limit_to}" if limit_to != :default
|
68
|
+
yql << "OFFSET #{offset}" if offset != :default
|
69
|
+
yql << "| sort(field='#{sort_columns}')" if sort_columns
|
70
|
+
yql.join " "
|
71
|
+
end
|
72
|
+
|
73
|
+
alias_method :to_s, :to_yql
|
74
|
+
|
75
|
+
private
|
76
|
+
def modify_clone(&block)
|
77
|
+
cloned_obj = self.deep_clone
|
78
|
+
cloned_obj.instance_eval &block
|
79
|
+
cloned_obj
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Yarel::Base" do
|
4
|
+
LocalSearch = Yarel::Table.new("local.search")
|
5
|
+
|
6
|
+
it "should create an instance of table" do
|
7
|
+
LocalSearch.should be_kind_of Yarel::Table
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "all" do
|
11
|
+
it "should raise exception back if response has errors" do
|
12
|
+
lambda {
|
13
|
+
LocalSearch.order('test').all
|
14
|
+
}.should raise_error(Yarel::Exception, "Cannot find required keys in where clause; expecting required keys: (query, longitude, latitude)")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should take the result part alone" do
|
18
|
+
GeoPlaces = Yarel::Table.new("geo.places")
|
19
|
+
GeoPlaces.where(:text => "north beach, san francisco").all.should == [{"name"=>"North Beach", "woeid"=>"2460640", "uri"=>"http://where.yahooapis.com/v1/place/2460640", "boundingBox"=>{"northEast"=>{"latitude"=>"37.808270", "longitude"=>"-122.399467"}, "southWest"=>{"latitude"=>"37.795399", "longitude"=>"-122.418381"}}, "postal"=>{"type"=>"Zip Code", "content"=>"94133"}, "country"=>{"code"=>"US", "type"=>"Country", "content"=>"United States"}, "placeTypeName"=>{"code"=>"22", "content"=>"Suburb"}, "centroid"=>{"latitude"=>"37.805939", "longitude"=>"-122.411118"}, "areaRank"=>"1", "admin1"=>{"code"=>"US-CA", "type"=>"State", "content"=>"California"}, "popRank"=>"0", "locality1"=>{"type"=>"Town", "content"=>"San Francisco"}, "admin2"=>{"code"=>"", "type"=>"County", "content"=>"San Francisco"}, "lang"=>"en-US", "locality2"=>{"type"=>"Suburb", "content"=>"North Beach"}, "admin3"=>nil}]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return mulitple records" do
|
23
|
+
LocalNewSearch = Yarel::Table.new("local.search")
|
24
|
+
|
25
|
+
LocalNewSearch.where(:zip => 94085, :query => 'pizza').limit(2).all.should == [
|
26
|
+
{"MapUrl"=>"http://maps.yahoo.com/maps_result?q1=1127+N+Lawrence+Expy+Sunnyvale+CA&gid1=21341983", "Distance"=>"1.28", "Longitude"=>"-121.996017", "City"=>"Sunnyvale", "Url"=>"http://local.yahoo.com/info-21341983-giovannis-pizzeria-sunnyvale", "Title"=>"Giovannis Pizzeria", "Latitude"=>"37.397058", "Phone"=>"(408) 734-4221", "id"=>"21341983", "Categories"=>{"Category"=>[{"id"=>"96926234", "content"=>"Carry Out & Take Out"}, {"id"=>"96926236", "content"=>"Restaurants"}, {"id"=>"96926243", "content"=>"Pizza"}]}, "BusinessUrl"=>"http://giovannisnypizza.com/", "ClickUrl"=>"http://local.yahoo.com/info-21341983-giovannis-pizzeria-sunnyvale", "Rating"=>{"TotalReviews"=>"44", "AverageRating"=>"4", "TotalRatings"=>"44", "LastReviewIntro"=>"this is one of the best pizzas i had..very tasty,crispy..value for the money spent..try it out once", "LastReviewDate"=>"1273837240"}, "Address"=>"1127 N Lawrence Expy", "State"=>"CA", "BusinessClickUrl"=>"http://giovannisnypizza.com/"},
|
27
|
+
{"MapUrl"=>"http://maps.yahoo.com/maps_result?q1=1155+Reed+Ave+Sunnyvale+CA&gid1=21332026", "Distance"=>"1.79", "Longitude"=>"-121.997904", "City"=>"Sunnyvale", "Url"=>"http://local.yahoo.com/info-21332026-vitos-famous-pizza-sunnyvale", "Title"=>"Vitos Famous Pizza", "Latitude"=>"37.367029", "Phone"=>"(408) 246-8800", "id"=>"21332026", "Categories"=>{"Category"=>[{"id"=>"96926190", "content"=>"Italian Restaurants"}, {"id"=>"96926234", "content"=>"Carry Out & Take Out"}, {"id"=>"96926236", "content"=>"Restaurants"}, {"id"=>"96926242", "content"=>"Fast Food"}, {"id"=>"96926243", "content"=>"Pizza"}]}, "BusinessUrl"=>"http://vitosfamouspizza.com/", "ClickUrl"=>"http://local.yahoo.com/info-21332026-vitos-famous-pizza-sunnyvale", "Rating"=>{"TotalReviews"=>"16", "AverageRating"=>"4.5", "TotalRatings"=>"16", "LastReviewIntro"=>"As an East Coaster, I am picky about my pizza, and this place is really a great find!", "LastReviewDate"=>"1247669752"}, "Address"=>"1155 Reed Ave", "State"=>"CA", "BusinessClickUrl"=>"http://vitosfamouspizza.com/"}
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should handle empty results" do
|
32
|
+
places = Yarel::Table.new("geo.places")
|
33
|
+
places.where(:text => "unkown place").all.should == []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "yarel" do
|
4
|
+
before(:each) do
|
5
|
+
@table_name = "answers.getbycategory"
|
6
|
+
@yarel_table = Yarel::Table.new(@table_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be chainable" do
|
10
|
+
@yarel_table.from("answers.new_table").should be_kind_of(Yarel::Table)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "return the yql" do
|
14
|
+
it "should be constructed taking the table name" do
|
15
|
+
yarel_table = Yarel::Table.new("ns.table_name").to_yql.should == "SELECT * FROM ns.table_name"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "for from" do
|
19
|
+
@yarel_table.from("answers.new_table").to_yql.should == "SELECT * FROM answers.new_table"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "for the latest from" do
|
23
|
+
@yarel_table.from("answers.new_table").from("answers.new_new_table").to_yql.should == "SELECT * FROM answers.new_new_table"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "for project" do
|
27
|
+
@yarel_table.project(:this_column).to_yql.should == "SELECT this_column FROM answers.getbycategory"
|
28
|
+
@yarel_table.project(:this_column, :that_column).to_yql.should == "SELECT this_column, that_column FROM answers.getbycategory"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "for limit" do
|
32
|
+
@yarel_table.limit(10).to_yql.should == "SELECT * FROM answers.getbycategory LIMIT 10"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "for limit should not mutate" do
|
36
|
+
q1 = @yarel_table.limit(10)
|
37
|
+
q2 = @yarel_table.limit(5)
|
38
|
+
q1.to_yql.should == "SELECT * FROM answers.getbycategory LIMIT 10"
|
39
|
+
q2.to_yql.should == "SELECT * FROM answers.getbycategory LIMIT 5"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "for offset along with limit" do
|
43
|
+
@yarel_table.limit(10, 20).to_yql.should == "SELECT * FROM answers.getbycategory LIMIT 20 OFFSET 10"
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "where" do
|
47
|
+
it "hash" do
|
48
|
+
@yarel_table.where(:this_column => 5).to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = '5'"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "string" do
|
52
|
+
@yarel_table.where("this_column = '5'").to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = '5'"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "array interpolation" do
|
56
|
+
@yarel_table.where(["this_column = ?", 'asd']).to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = 'asd'"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should multiple interpolation" do
|
60
|
+
@yarel_table.where(["this_column = ? AND that_column = ?", 5, 10]).to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = '5' AND that_column = '10'"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be able to call multiple times" do
|
64
|
+
@yarel_table.where(["this_column = ?", 5]).project(:this_column).where(:that_column => 10).to_yql.should == "SELECT this_column FROM answers.getbycategory WHERE this_column = '5' AND that_column = '10'"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not mutate the current object" do
|
68
|
+
@yarel_table.where(["this_column = ?", 5]).to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = '5'"
|
69
|
+
@yarel_table.where(["this_column = ? AND that_column = ?", 5, 10]).to_yql.should == "SELECT * FROM answers.getbycategory WHERE this_column = '5' AND that_column = '10'"
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "where with sub queries" do
|
73
|
+
it "as a hash" do
|
74
|
+
@yarel_table.where(:this_column => Yarel::Table.new(:sub_table).project("sub_table_column")).to_yql.should ==
|
75
|
+
"SELECT * FROM answers.getbycategory WHERE this_column in ( SELECT sub_table_column FROM sub_table )"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "for sort" do
|
81
|
+
@yarel_table.sort('Rating.AverageRating').to_s.should == "SELECT * FROM answers.getbycategory | sort(field='Rating.AverageRating')"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
describe "yarel module" do
|
88
|
+
it "should construct table object" do
|
89
|
+
Yarel::GeoLocation.should be_kind_of Yarel::Table
|
90
|
+
end
|
91
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yarel
|
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
|
+
- Selvakumar Natesan
|
13
|
+
- Sharanya Sennimalai
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-25 00:00:00 +05:30
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
- beta4
|
33
|
+
version: 3.0.0.beta4
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activemodel
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 3
|
45
|
+
- 0
|
46
|
+
- 0
|
47
|
+
- beta4
|
48
|
+
version: 3.0.0.beta4
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: rspec
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Object-relational mapper for Yahoo Query Language - A Object Relation Mapper for YQL in ruby, very similar to ActiveRecord 3.0.0 with chainability and lazy evaluation of queries
|
64
|
+
email:
|
65
|
+
- k.n.selvakumar@gmail.com
|
66
|
+
- s.sharanya@gmail.com
|
67
|
+
executables: []
|
68
|
+
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files: []
|
72
|
+
|
73
|
+
files:
|
74
|
+
- lib/yarel/base.rb
|
75
|
+
- lib/yarel/connection.rb
|
76
|
+
- lib/yarel/extensions/object_extensions.rb
|
77
|
+
- lib/yarel/table.rb
|
78
|
+
- lib/yarel.rb
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: http://github.com/selvakn/yarel
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
segments:
|
93
|
+
- 1
|
94
|
+
- 8
|
95
|
+
- 7
|
96
|
+
version: 1.8.7
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
requirements: []
|
105
|
+
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.3.6
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Object-relational mapper for Yahoo Query Language
|
111
|
+
test_files:
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
- spec/yarel/base_spec.rb
|
114
|
+
- spec/yarel/yarel_spec.rb
|