yarel 0.0.1

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.
@@ -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")
@@ -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
@@ -0,0 +1,7 @@
1
+ module ObjectExtensions
2
+ def deep_clone
3
+ Marshal.load( Marshal.dump(self))
4
+ end
5
+ end
6
+
7
+ Object.send :include, ObjectExtensions
@@ -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
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.join File.dirname(__FILE__) + "/../lib"
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'yarel'
@@ -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