linqr 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -0
- data/Gemfile.lock +26 -0
- data/README.rdoc +90 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/examples/linqr2enumerable/ordering_operators_spec.rb +54 -0
- data/examples/linqr2enumerable/projection_operators_spec.rb +54 -0
- data/lib/expression_evaluator_base.rb +45 -0
- data/lib/group_by.rb +1 -0
- data/lib/lazy_enumerator.rb +31 -0
- data/lib/linqr.rb +55 -0
- data/lib/linqr_exp.rb +84 -0
- data/lib/providers/active_record/active_record_expression_evaluator.rb +58 -0
- data/lib/providers/active_record/activerecord_provider.rb +48 -0
- data/lib/providers/enumerable_expression_evaluator.rb +46 -0
- data/lib/providers/enumerable_provider.rb +57 -0
- data/lib/providers/groupon/groupon_provider.rb +41 -0
- data/lib/providers/hash_provider.rb +22 -0
- data/lib/source_name_evaluator.rb +36 -0
- data/spec/active_record_provider_spec.rb +93 -0
- data/spec/database.yml +6 -0
- data/spec/enumerable_expression_evaluator_spec.rb +15 -0
- data/spec/enumerable_provider_spec.rb +101 -0
- data/spec/groupon_provider_spec.rb +17 -0
- data/spec/order.rb +2 -0
- data/spec/schema.rb +19 -0
- data/spec/spec_helper.rb +9 -0
- metadata +117 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.9.2)
|
11
|
+
rspec (2.4.0)
|
12
|
+
rspec-core (~> 2.4.0)
|
13
|
+
rspec-expectations (~> 2.4.0)
|
14
|
+
rspec-mocks (~> 2.4.0)
|
15
|
+
rspec-core (2.4.0)
|
16
|
+
rspec-expectations (2.4.0)
|
17
|
+
diff-lcs (~> 1.1.2)
|
18
|
+
rspec-mocks (2.4.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
bundler (~> 1.0.0)
|
25
|
+
jeweler (~> 1.6.4)
|
26
|
+
rspec (~> 2.4.0)
|
data/README.rdoc
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
= Linqr - Query Comprehensions for Ruby
|
2
|
+
|
3
|
+
Linq ( http://msdn.microsoft.com/en-us/vcsharp/aa336746 )like sytax for ruby.
|
4
|
+
|
5
|
+
Linq in C# is a great example of bringing a uniform query model to
|
6
|
+
different sources of Data. Different providers can be hooked into the
|
7
|
+
model so developers can still use the familiar sytax to query the data
|
8
|
+
source instead of learning a whole new api.
|
9
|
+
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
=== Querying enumerable
|
14
|
+
* Quering an array
|
15
|
+
|
16
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
17
|
+
output = _{
|
18
|
+
from x
|
19
|
+
in_ numbers
|
20
|
+
where (x == 3 || x == 5 || x == 8) && ( x % 2) == 0
|
21
|
+
select x * 3
|
22
|
+
}
|
23
|
+
output.should == [24]
|
24
|
+
|
25
|
+
* Quering a hash
|
26
|
+
|
27
|
+
hash = {:a => 1 , :b => 2 , :c => 3}
|
28
|
+
output = _{
|
29
|
+
from k,v
|
30
|
+
in_ hash
|
31
|
+
where v == 3
|
32
|
+
select k
|
33
|
+
}
|
34
|
+
output.should == [:c]
|
35
|
+
|
36
|
+
* Grouping(select can select into anonymous types)
|
37
|
+
|
38
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
39
|
+
number_groups = _{
|
40
|
+
from n
|
41
|
+
in_ numbers
|
42
|
+
where n > 3 && n < 100
|
43
|
+
group_by n % 5 => :g
|
44
|
+
select :remainder => g.key , :values => g.values
|
45
|
+
}
|
46
|
+
number_groups.collect(&:remainder).should == [0, 4, 1, 3, 2]
|
47
|
+
|
48
|
+
* Ordering
|
49
|
+
|
50
|
+
words = [ "cherry", "apple", "blueberry" ]
|
51
|
+
sorted_words = _{
|
52
|
+
from word
|
53
|
+
in_ words
|
54
|
+
order_by word.length
|
55
|
+
select word
|
56
|
+
}
|
57
|
+
sorted_words.should == ["apple", "cherry", "blueberry"]
|
58
|
+
* Selecting into anonymous types
|
59
|
+
|
60
|
+
products = [Product.new("shoes", 1.75), Product.new("glasses", 55.55), Product.new("pencil", 5.20)]
|
61
|
+
|
62
|
+
cheap_product_names = _{
|
63
|
+
from p
|
64
|
+
in_ products
|
65
|
+
where p.price < 10
|
66
|
+
select :name => "Cheap - #{p.name}" , :new_price => p.price + 10
|
67
|
+
}
|
68
|
+
|
69
|
+
cheap_product_names.collect(&:name).should == ["Cheap - shoes","Cheap - pencil"]
|
70
|
+
cheap_product_names.collect(&:new_price).should == [11.75, 15.20]
|
71
|
+
|
72
|
+
=== Querying ActiveRecord Models
|
73
|
+
All the examples listed for enumerable should work with activerecord models too
|
74
|
+
|
75
|
+
grouped_by_name = _{
|
76
|
+
from o
|
77
|
+
in_ Order
|
78
|
+
order_by o.name
|
79
|
+
group_by o.name => :g
|
80
|
+
select :name => g.key , :orders => g.values
|
81
|
+
}
|
82
|
+
|
83
|
+
=== Querying Groupon Api
|
84
|
+
output = _{·
|
85
|
+
from deal
|
86
|
+
in_ GrouponDeals
|
87
|
+
where deal.lat == 38.8339 && deal.lng == -104.821·
|
88
|
+
select deal
|
89
|
+
}
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
require 'rake'
|
12
|
+
|
13
|
+
require 'jeweler'
|
14
|
+
Jeweler::Tasks.new do |gem|
|
15
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
16
|
+
gem.name = "linqr"
|
17
|
+
gem.homepage = "http://github.com/suryagaddipati/linqr"
|
18
|
+
gem.license = "MIT"
|
19
|
+
gem.summary = %Q{Query Comprehensions for ruby}
|
20
|
+
gem.description = %Q{Linq like sytax for querying multiple-datasources}
|
21
|
+
gem.email = "surya.gaddipati@gmail.com"
|
22
|
+
gem.authors = ["surya"]
|
23
|
+
# dependencies defined in Gemfile
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
|
28
|
+
desc 'Default: run specs.'
|
29
|
+
task :default => :spec
|
30
|
+
|
31
|
+
desc "Run specs"
|
32
|
+
RSpec::Core::RakeTask.new do |t|
|
33
|
+
t.pattern = ["./spec/**/*_spec.rb","./examples/**/*_spec.rb"]
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Generate code coverage"
|
37
|
+
RSpec::Core::RakeTask.new(:coverage) do |t|
|
38
|
+
t.pattern = ["./spec/**/*_spec.rb","./examples/**/*_spec.rb"]
|
39
|
+
t.rcov = true
|
40
|
+
t.rcov_opts = ['--exclude', 'spec']
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "linqr #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'linqr'
|
2
|
+
require 'spec_helper'
|
3
|
+
describe "order by" do
|
4
|
+
Item = Struct.new(:name , :price, :discount_price)
|
5
|
+
it "should order by the order by operator" do
|
6
|
+
words = [ "cherry", "apple", "blueberry" ]
|
7
|
+
sorted_words = __{
|
8
|
+
from word in_ words order_by word
|
9
|
+
select word
|
10
|
+
}
|
11
|
+
sorted_words.should == ["apple", "blueberry","cherry" ]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "simple-2" do
|
15
|
+
words = [ "cherry", "apple", "blueberry" ]
|
16
|
+
sorted_words = __{
|
17
|
+
from word
|
18
|
+
in_ words
|
19
|
+
order_by word.length
|
20
|
+
select word
|
21
|
+
}
|
22
|
+
sorted_words.should == ["apple", "cherry", "blueberry"]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "descending" do
|
26
|
+
doubles = [ 1.7, 2.3, 1.9, 4.1, 2.9 ]
|
27
|
+
desc_doubles = __{
|
28
|
+
from d
|
29
|
+
in_ doubles
|
30
|
+
order_by d => descending
|
31
|
+
select d
|
32
|
+
}
|
33
|
+
desc_doubles.should == [4.1, 2.9, 2.3, 1.9, 1.7]
|
34
|
+
end
|
35
|
+
it "then-by-descending" do
|
36
|
+
products = [Item.new("bag", 9.00,5.00),Item.new("shoes", 5.00,2.00), Item.new("apple", 5.00,4.00), Item.new("cup", 25.00,14.00)]
|
37
|
+
__{
|
38
|
+
from p
|
39
|
+
in_ products
|
40
|
+
order_by p.price, p.discount_price
|
41
|
+
select p
|
42
|
+
}.collect(&:name).should == ["shoes","apple", "bag", "cup"]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "then-by" do
|
46
|
+
products = [Item.new("bag", 9.00,5.00),Item.new("apple", 5.00,2.00), Item.new("shoes", 5.00,4.00), Item.new("cup", 25.00,5.00)]
|
47
|
+
__{
|
48
|
+
from p
|
49
|
+
in_ products
|
50
|
+
order_by p.price, p.discount_price => descending
|
51
|
+
select p
|
52
|
+
}.collect(&:name).should == ["cup","bag","shoes","apple"]
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'linqr'
|
2
|
+
describe "Projection operators" do
|
3
|
+
it "Simple 1" do
|
4
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
5
|
+
output = _{
|
6
|
+
from n
|
7
|
+
in_ numbers
|
8
|
+
select n + 1
|
9
|
+
}.to_a
|
10
|
+
output.should == [6, 5, 2, 4, 10, 9, 7, 8, 3, 1]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "Simple 2" do
|
14
|
+
Product = Struct.new(:name, :price)
|
15
|
+
products = [Product.new("shoes", 1.75), Product.new("glasses", 55.55), Product.new("pencil", 5.20)]
|
16
|
+
|
17
|
+
product_names = _{
|
18
|
+
from p
|
19
|
+
in_ products
|
20
|
+
select p.name
|
21
|
+
}.to_a
|
22
|
+
product_names.should == ["shoes","glasses","pencil"]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Select - Transformation" do
|
26
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
27
|
+
strings = [ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" ]
|
28
|
+
text_nums = _{
|
29
|
+
from n
|
30
|
+
in_ numbers
|
31
|
+
select strings[n]
|
32
|
+
}.to_a
|
33
|
+
text_nums.should == ["five", "four", "one", "three", "nine", "eight", "six", "seven", "two", "zero"]
|
34
|
+
end
|
35
|
+
it "should return a thunk" do
|
36
|
+
natural_numbers = Enumerator.new do |yielder|
|
37
|
+
number = 1
|
38
|
+
loop do
|
39
|
+
yielder.yield number
|
40
|
+
number += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
plus_ones = _{
|
45
|
+
from n
|
46
|
+
where n < 5
|
47
|
+
in_ natural_numbers
|
48
|
+
select n
|
49
|
+
}.take(4)
|
50
|
+
|
51
|
+
plus_ones.to_a.should == [1,2,3,4]
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class ExpressionEvaluator
|
2
|
+
|
3
|
+
def initialize(linq_exp)
|
4
|
+
@linq_exp = linq_exp
|
5
|
+
end
|
6
|
+
|
7
|
+
def visit_symbol(node)
|
8
|
+
node.value
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_variable(node)
|
12
|
+
@linq_exp.variable_val(node.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_integer(node)
|
16
|
+
node.value
|
17
|
+
end
|
18
|
+
|
19
|
+
def visit_float(node)
|
20
|
+
node.value
|
21
|
+
end
|
22
|
+
def visit_unary(node)
|
23
|
+
target = node.operand.visit(self)
|
24
|
+
target.send("#{node.operator.to_s}@")
|
25
|
+
end
|
26
|
+
def visit_arg(node)
|
27
|
+
node.arg.visit(self)
|
28
|
+
end
|
29
|
+
def visit_string(node)
|
30
|
+
output = ""
|
31
|
+
node.each do |e|
|
32
|
+
output << e.visit(self)
|
33
|
+
end
|
34
|
+
output
|
35
|
+
end
|
36
|
+
|
37
|
+
def visit_stringcontent(node)
|
38
|
+
node.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def visit_statements(node)
|
42
|
+
binary_exp = node.elements.first
|
43
|
+
binary_exp.visit(self)
|
44
|
+
end
|
45
|
+
end
|
data/lib/group_by.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Grouped = Struct.new(:key,:values)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Enumerator
|
2
|
+
|
3
|
+
def lazy_sort_by(&block)
|
4
|
+
Enumerator.new do |yielder|
|
5
|
+
index = 0
|
6
|
+
sorted = sort_by(&block)
|
7
|
+
loop do
|
8
|
+
yielder.yield sorted[index]
|
9
|
+
index += 1
|
10
|
+
break if sorted.size == index
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def lazy_select(&block)
|
16
|
+
Enumerator.new do |yielder|
|
17
|
+
self.each do |val|
|
18
|
+
yielder.yield(val) if block.call(val)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def lazy_map(&block)
|
24
|
+
Enumerator.new do |yielder|
|
25
|
+
self.each do |value|
|
26
|
+
yielder.yield(block.call(value))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/lib/linqr.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
require 'providers/enumerable_provider'
|
3
|
+
require 'providers/hash_provider'
|
4
|
+
require 'linqr_exp'
|
5
|
+
require 'ripper2ruby'
|
6
|
+
class Ruby::Node
|
7
|
+
def visit(visitor)
|
8
|
+
class_name=self.class.name.split('::').size == 2 ? self.class.name.split('::')[1]: self.class.name
|
9
|
+
visitor.send("visit_#{class_name.downcase}".to_sym, self)
|
10
|
+
end
|
11
|
+
def evaluate_source_name(visitor)
|
12
|
+
visitor.send("source_name_#{self.class.name.split('::')[1].downcase}".to_sym, self)
|
13
|
+
end
|
14
|
+
def to_sym
|
15
|
+
to_ruby.to_sym
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
class OrderBy
|
21
|
+
def initialize(node)
|
22
|
+
@node = node
|
23
|
+
end
|
24
|
+
|
25
|
+
def expressions
|
26
|
+
descending?? (@node.arguments[0...-1] << last_arg.first.key): @node.arguments
|
27
|
+
end
|
28
|
+
|
29
|
+
def descending?
|
30
|
+
last_arg.is_a? Ruby::Hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_arg
|
34
|
+
@node.arguments.last.arg
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class GroupBy < Ruby::Node
|
39
|
+
def initialize(node)
|
40
|
+
@node = node
|
41
|
+
end
|
42
|
+
def expression
|
43
|
+
return @node.arg.first.key if @node.arg.is_a? Ruby::Hash
|
44
|
+
@node
|
45
|
+
end
|
46
|
+
def grouping_var
|
47
|
+
@node.arg.first.value.first.to_sym
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Object
|
52
|
+
def _(&proc_exp)
|
53
|
+
LinqrExp.new(proc_exp).evaluate
|
54
|
+
end
|
55
|
+
end
|
data/lib/linqr_exp.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'sourcify'
|
2
|
+
require 'ripper'
|
3
|
+
require 'source_name_evaluator'
|
4
|
+
class LinqrExp
|
5
|
+
|
6
|
+
%w(where from select).each do | q |
|
7
|
+
send(:define_method,q.to_sym) { fexp(@exp,q) }
|
8
|
+
send(:define_method,(q+"?").to_sym) {!fcall(@exp,q).nil?}
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_vars
|
12
|
+
@variables ||= {}
|
13
|
+
Proc.new do |args|
|
14
|
+
args= [args] unless args.is_a? Array
|
15
|
+
args.each_with_index do |param, idx|
|
16
|
+
@variables[variables[idx].to_s]= param
|
17
|
+
end
|
18
|
+
yield *args
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_variable(var,val)
|
23
|
+
@variables ||= {}
|
24
|
+
@variables[var] = val
|
25
|
+
end
|
26
|
+
|
27
|
+
def variable_val(var_name)
|
28
|
+
if (@variables && var = @variables[var_name])
|
29
|
+
var
|
30
|
+
else
|
31
|
+
@binding.eval(var_name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def group_by
|
36
|
+
GroupBy.new(fexp(@exp,"group_by"))
|
37
|
+
end
|
38
|
+
|
39
|
+
def group_by?
|
40
|
+
!fcall(@exp,"group_by").nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def order_by
|
44
|
+
OrderBy.new(fcall(@exp,"order_by"))
|
45
|
+
end
|
46
|
+
|
47
|
+
def order_by?
|
48
|
+
!fcall(@exp,"order_by").nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
attr_reader :binding
|
53
|
+
def initialize(proc_exp)
|
54
|
+
@exp = Ripper::RubyBuilder.build(proc_exp.to_source)
|
55
|
+
@binding = proc_exp.binding
|
56
|
+
end
|
57
|
+
|
58
|
+
def evaluate
|
59
|
+
source.linqr_provider.evaluate(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def variable
|
63
|
+
variables.first
|
64
|
+
end
|
65
|
+
def variables
|
66
|
+
fcall(@exp,"from").arguments.collect(&:name)
|
67
|
+
end
|
68
|
+
def fcall(exp, fname)
|
69
|
+
exp.select(Ruby::Call).select {|call|call.identifier && call.token == fname}.first
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def fexp(exp, fname)
|
74
|
+
f_arg = fcall(exp,fname).arguments.first
|
75
|
+
f_arg # make this less confusing
|
76
|
+
end
|
77
|
+
def source
|
78
|
+
token =fcall(@exp,"in_").arguments.first
|
79
|
+
source_name = token.evaluate_source_name(SourceNameEvaluator.new)
|
80
|
+
@binding.eval(source_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'expression_evaluator_base'
|
2
|
+
class ActiveRecordExpressionEvaluator < ExpressionEvaluator
|
3
|
+
attr_reader :conditions
|
4
|
+
def initialize(linq_exp)
|
5
|
+
@binding = linq_exp.binding
|
6
|
+
@conditions = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def visit_argslist(node)
|
10
|
+
node.first.visit(self)
|
11
|
+
end
|
12
|
+
def visit_variable(node)
|
13
|
+
@binding.eval(node.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit_integer(node)
|
17
|
+
node.value
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_binary(node)
|
21
|
+
right_val = node.right.visit(self)
|
22
|
+
left_val = node.left.visit(self)
|
23
|
+
@conditions[left_val] = right_val
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_arg(node)
|
27
|
+
node.arg.visit(self)
|
28
|
+
end
|
29
|
+
def visit_string(node)
|
30
|
+
node.value
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_call(node)
|
34
|
+
call = node.identifier.to_sym
|
35
|
+
return call unless call == :member?
|
36
|
+
target = node.target.visit(self)
|
37
|
+
argument = node.arguments.visit(self)
|
38
|
+
@conditions[argument] = target
|
39
|
+
end
|
40
|
+
def visit_array(node)
|
41
|
+
node.value
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_statements(node)
|
45
|
+
binary_exp = node.elements.first
|
46
|
+
binary_exp.visit(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class ArGroupByExpressionEvaluator < ActiveRecordExpressionEvaluator
|
52
|
+
attr_reader :grouping_var , :group_by
|
53
|
+
def visit_hash(node)
|
54
|
+
@grouping_var = node.first.value.visit(self)
|
55
|
+
@group_by = node.first.key.visit(self)
|
56
|
+
@group_by
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'group_by'
|
3
|
+
require 'providers/enumerable_provider'
|
4
|
+
require 'providers/active_record/active_record_expression_evaluator'
|
5
|
+
class ActiveRecordProvider
|
6
|
+
def initialize(active_record_class)
|
7
|
+
@active_record_class = active_record_class
|
8
|
+
end
|
9
|
+
def evaluate(linq_exp)
|
10
|
+
query_params = {}
|
11
|
+
evaluator = ActiveRecordExpressionEvaluator.new(linq_exp)
|
12
|
+
if (linq_exp.where?)
|
13
|
+
linq_exp.where.visit(evaluator)
|
14
|
+
query_params.merge!(:conditions =>evaluator.conditions)
|
15
|
+
end
|
16
|
+
|
17
|
+
group_by_evaluator = ArGroupByExpressionEvaluator.new(linq_exp)
|
18
|
+
if(linq_exp.group_by?)
|
19
|
+
query_params.merge!(:group =>linq_exp.group_by.visit(group_by_evaluator))
|
20
|
+
end
|
21
|
+
if(linq_exp.order_by?)
|
22
|
+
query_params.merge!(:order =>linq_exp.order_by.visit(evaluator))
|
23
|
+
end
|
24
|
+
|
25
|
+
selected_values = @active_record_class.find(:all,query_params)
|
26
|
+
|
27
|
+
if (linq_exp.group_by?)
|
28
|
+
grouped_values = selected_values.group_by(&group_by_evaluator.group_by)
|
29
|
+
grouped_values.collect do |(k,v)|
|
30
|
+
Object.send(:define_method,group_by_evaluator.grouping_var) { Grouped.new(k,v) }
|
31
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
32
|
+
end
|
33
|
+
else
|
34
|
+
selected_values.collect do |e|
|
35
|
+
Object.send(:define_method,linq_exp.variable.to_sym) { e }
|
36
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class ActiveRecord::Base
|
43
|
+
def self.linqr_provider
|
44
|
+
ActiveRecordProvider.new(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'expression_evaluator_base'
|
3
|
+
class EnumerableExpessionEvaluator < ExpressionEvaluator
|
4
|
+
|
5
|
+
def visit_groupby(node)
|
6
|
+
node.expression.visit(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def visit_hash(node)
|
10
|
+
record = OpenStruct.new
|
11
|
+
node.elements.each do |e|
|
12
|
+
key = e.key.visit(self)
|
13
|
+
value = e.value.visit(self)
|
14
|
+
record.send("#{key.to_s}=".to_sym,value)
|
15
|
+
end
|
16
|
+
record
|
17
|
+
end
|
18
|
+
|
19
|
+
def visit_argslist(node)
|
20
|
+
node.map {|arg| arg.visit(self)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_binary(node)
|
24
|
+
right_val = node.right.visit(self)
|
25
|
+
left_val = node.left.visit(self)
|
26
|
+
if node.operator.to_sym == :and
|
27
|
+
left_val && right_val
|
28
|
+
elsif node.operator.to_sym == :or
|
29
|
+
left_val || right_val
|
30
|
+
else
|
31
|
+
left_val.send(node.operator.to_ruby.to_sym, right_val)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_call(node)
|
36
|
+
target = node.target.visit(self)
|
37
|
+
method_name = node.identifier ? node.identifier.to_sym : :[]
|
38
|
+
if (node.arguments)
|
39
|
+
arguments = node.arguments.collect { |x| x.visit(self) }
|
40
|
+
target.send(method_name, *arguments)
|
41
|
+
else
|
42
|
+
target.send(method_name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'providers/enumerable_expression_evaluator'
|
2
|
+
require 'lazy_enumerator'
|
3
|
+
require 'group_by'
|
4
|
+
class EnumerableProvider
|
5
|
+
def initialize(enumerable)
|
6
|
+
@enumerable = enumerable.to_enum
|
7
|
+
end
|
8
|
+
def handle_where(linq_exp)
|
9
|
+
evaluator = EnumerableExpessionEvaluator.new(linq_exp)
|
10
|
+
@enumerable.lazy_select(&linq_exp.with_vars do|e|
|
11
|
+
linq_exp.where.visit(evaluator)
|
12
|
+
end)
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle_order_by(linq_exp,filtered_values)
|
16
|
+
order_by = linq_exp.order_by
|
17
|
+
order_by.expressions.reduce(filtered_values) do |values, sort_exp|
|
18
|
+
values.lazy_sort_by(&linq_exp.with_vars do|e|
|
19
|
+
sort_val = sort_exp.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
20
|
+
order_by.descending?? 1 - sort_val : sort_val
|
21
|
+
end)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_group_by(linq_exp,filtered_values)
|
26
|
+
grouped_values = filtered_values.group_by(&linq_exp.with_vars do |e|
|
27
|
+
linq_exp.group_by.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
28
|
+
end)
|
29
|
+
|
30
|
+
grouped_values.collect do |(k,v)|
|
31
|
+
linq_exp.set_variable(linq_exp.group_by.grouping_var.to_s, Grouped.new(k,v) )
|
32
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def handle_select(linq_exp,filtered_values)
|
37
|
+
filtered_values.lazy_map(&linq_exp.with_vars do |e|
|
38
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
39
|
+
end)
|
40
|
+
end
|
41
|
+
|
42
|
+
def evaluate (linq_exp)
|
43
|
+
filtered_values =linq_exp.where?? handle_where(linq_exp) : @enumerable
|
44
|
+
filtered_values = linq_exp.order_by?? handle_order_by(linq_exp,filtered_values) : filtered_values
|
45
|
+
if (linq_exp.group_by?)
|
46
|
+
handle_group_by(linq_exp,filtered_values)
|
47
|
+
else
|
48
|
+
handle_select(linq_exp,filtered_values)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module Enumerable
|
54
|
+
def linqr_provider
|
55
|
+
EnumerableProvider.new(self)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'groupon'
|
2
|
+
require 'expression_evaluator_base'
|
3
|
+
class GrouponDeals
|
4
|
+
|
5
|
+
def self.linqr_provider
|
6
|
+
self
|
7
|
+
end
|
8
|
+
def self.evaluate(linq_exp)
|
9
|
+
Groupon.api_key = '966a0273f2974c725e25d507d4e07daabcb0ee00'
|
10
|
+
evaluator = GrouponExpressionEvaluator.new(linq_exp)
|
11
|
+
linq_exp.where.visit(evaluator)
|
12
|
+
selected_values = Groupon.deals(evaluator.conditions)
|
13
|
+
#puts evaluator.conditions.inspect
|
14
|
+
selected_values.collect do |e|
|
15
|
+
Object.send(:define_method,linq_exp.variable.to_sym) { e }
|
16
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class GrouponExpressionEvaluator < ExpressionEvaluator
|
23
|
+
attr_reader :conditions
|
24
|
+
def initialize(linq_exp)
|
25
|
+
@conditions = {}
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def visit_binary(node)
|
30
|
+
right_val = node.right.visit(self)
|
31
|
+
left_val = node.left.visit(self)
|
32
|
+
@conditions[left_val] = right_val
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def visit_call(node)
|
37
|
+
node.identifier.to_s.to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'providers/enumerable_provider'
|
2
|
+
|
3
|
+
class HashProvider < EnumerableProvider
|
4
|
+
|
5
|
+
def handle_where(linq_exp)
|
6
|
+
filtered_values = @enumerable.lazy_select(&linq_exp.with_vars do|k,v|
|
7
|
+
linq_exp.where.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
8
|
+
end)
|
9
|
+
end
|
10
|
+
|
11
|
+
def handle_select(linq_exp,filtered_values)
|
12
|
+
filtered_values.lazy_map(&linq_exp.with_vars do |k,v|
|
13
|
+
linq_exp.select.visit(EnumerableExpessionEvaluator.new(linq_exp))
|
14
|
+
end)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Hash
|
19
|
+
def linqr_provider
|
20
|
+
HashProvider.new(self)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ripper2ruby'
|
2
|
+
class SourceNameEvaluator
|
3
|
+
def source_name_arg(node)
|
4
|
+
node.name
|
5
|
+
end
|
6
|
+
|
7
|
+
def source_name_const(node)
|
8
|
+
node.identifier.to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
def source_name_variable(node)
|
12
|
+
node.token.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Ruby::Arg
|
17
|
+
def name
|
18
|
+
arg.name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
class Ruby::Call
|
22
|
+
def name
|
23
|
+
identifier.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
class Ruby::Variable
|
27
|
+
def name
|
28
|
+
to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Ruby::Const
|
33
|
+
def name
|
34
|
+
identifier.to_s
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'linqr'
|
3
|
+
require 'providers/active_record/activerecord_provider'
|
4
|
+
require 'spec_helper'
|
5
|
+
ENV['DB'] ||= "mysql"
|
6
|
+
database_yml = File.expand_path('../database.yml', __FILE__)
|
7
|
+
if File.exists?(database_yml)
|
8
|
+
active_record_configuration = YAML.load_file(database_yml)[ENV['DB']]
|
9
|
+
ActiveRecord::Base.establish_connection(active_record_configuration)
|
10
|
+
ActiveRecord::Base.silence do
|
11
|
+
ActiveRecord::Migration.verbose = false
|
12
|
+
load(File.dirname(__FILE__) + '/schema.rb')
|
13
|
+
load(File.dirname(__FILE__) + '/order.rb')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
describe "ActiveRecord Provider" do
|
17
|
+
before {
|
18
|
+
ActiveRecord::Base.connection.execute "DELETE FROM Orders"
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
it "should select from active-record" do
|
23
|
+
Order.create(:name => "first")
|
24
|
+
Order.create(:name => "second")
|
25
|
+
second_order = Order.find(:all , :conditions => {:name => "second"})
|
26
|
+
|
27
|
+
output = _{
|
28
|
+
from o
|
29
|
+
in_ Order
|
30
|
+
where o.name == "second"
|
31
|
+
select o
|
32
|
+
|
33
|
+
}
|
34
|
+
output.should == second_order
|
35
|
+
end
|
36
|
+
describe "group_by" do
|
37
|
+
it "should group by the attribute" do
|
38
|
+
Order.create(:name => "first")
|
39
|
+
Order.create(:name => "second")
|
40
|
+
Order.create(:name => "second")
|
41
|
+
Order.create(:name => "third")
|
42
|
+
|
43
|
+
expected_grouped_orders = Order.find(:all , :group => :name)
|
44
|
+
|
45
|
+
grouped_by_name = _{
|
46
|
+
from o
|
47
|
+
in_ Order
|
48
|
+
group_by o.name => :g
|
49
|
+
select :name => g.key , :orders => g.values
|
50
|
+
}
|
51
|
+
grouped_by_name.collect(&:name).should == ["first", "second", "third"]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "order by" do
|
56
|
+
|
57
|
+
it "should order by the order by operator" do
|
58
|
+
Order.create(:name => "berry")
|
59
|
+
Order.create(:name => "apple")
|
60
|
+
Order.create(:name => "peach")
|
61
|
+
Order.create(:name => "cherry")
|
62
|
+
|
63
|
+
expected_output = Order.find(:all , :order => :name).collect(&:name)
|
64
|
+
|
65
|
+
ordered_names = _{
|
66
|
+
from o
|
67
|
+
in_ Order
|
68
|
+
order_by o.name
|
69
|
+
select o.name
|
70
|
+
}
|
71
|
+
ordered_names.should == expected_output
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "in" do
|
76
|
+
it "should retrive all object" do
|
77
|
+
Order.create(:name => "first")
|
78
|
+
Order.create(:name => "second")
|
79
|
+
Order.create(:name => "third")
|
80
|
+
|
81
|
+
|
82
|
+
first_second = Order.find(:all , :conditions => {:name => ["second", "first"]})
|
83
|
+
|
84
|
+
output = _{
|
85
|
+
from o
|
86
|
+
in_ Order
|
87
|
+
where ["second", "first"].member?(o.name)
|
88
|
+
select o
|
89
|
+
}
|
90
|
+
output.should == first_second
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'linqr'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe EnumerableExpessionEvaluator do
|
5
|
+
|
6
|
+
describe "#visit_binary" do
|
7
|
+
it "should invoke the operator" do
|
8
|
+
x = true
|
9
|
+
y = false
|
10
|
+
p = lambda{ x && y }
|
11
|
+
|
12
|
+
#puts EnumerableExpessionEvaluator.new LinqrExp.new(p)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'linqr'
|
2
|
+
require 'spec_helper'
|
3
|
+
describe "linqr" do
|
4
|
+
it "simple binary expression" do
|
5
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
6
|
+
output = __{
|
7
|
+
from x
|
8
|
+
in_ numbers
|
9
|
+
where x > 1
|
10
|
+
select x * 3
|
11
|
+
}
|
12
|
+
output.should == [15, 12, 9, 27, 24, 18, 21, 6]
|
13
|
+
end
|
14
|
+
it"a little complex binary expression"do
|
15
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
16
|
+
output = __{
|
17
|
+
from x
|
18
|
+
in_ numbers
|
19
|
+
where (x % 2) > 0
|
20
|
+
select x
|
21
|
+
}
|
22
|
+
output.should == [ 5, 1, 3, 9, 7 ]
|
23
|
+
end
|
24
|
+
it"compound 'or' binary expression"do
|
25
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
26
|
+
output = __{
|
27
|
+
from x
|
28
|
+
in_ numbers
|
29
|
+
where (x == 3 || x == 5 || x == 8) && ( x % 2) == 0
|
30
|
+
select x
|
31
|
+
}
|
32
|
+
output.should == [8]
|
33
|
+
end
|
34
|
+
it"compound 'and' binary expression"do
|
35
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
36
|
+
output = __{
|
37
|
+
from x
|
38
|
+
in_ numbers
|
39
|
+
where x > 3 && x <= 5
|
40
|
+
select x.to_s
|
41
|
+
}
|
42
|
+
output.should == [ '5','4']
|
43
|
+
end
|
44
|
+
|
45
|
+
it "query associative array" do
|
46
|
+
hash = {:a => 1 , :b => 2 , :c => 3}
|
47
|
+
output = __{
|
48
|
+
from k,v
|
49
|
+
in_ hash
|
50
|
+
where v == 3
|
51
|
+
select k
|
52
|
+
}
|
53
|
+
output.should == [:c]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should work with models" do
|
57
|
+
Product = Struct.new(:name, :price)
|
58
|
+
products = [Product.new("shoes", 1.75), Product.new("glasses", 55.55), Product.new("pencil", 5.20)]
|
59
|
+
|
60
|
+
cheap_product_names = __{
|
61
|
+
from p
|
62
|
+
in_ products
|
63
|
+
where p.price < 10
|
64
|
+
select p.name
|
65
|
+
}
|
66
|
+
|
67
|
+
cheap_product_names.should == ["shoes","pencil"]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "select into anonymous types" do
|
71
|
+
Product = Struct.new(:name, :price)
|
72
|
+
products = [Product.new("shoes", 1.75), Product.new("glasses", 55.55), Product.new("pencil", 5.20)]
|
73
|
+
|
74
|
+
cheap_product_names = __{
|
75
|
+
from p
|
76
|
+
in_ products
|
77
|
+
where p.price < 10
|
78
|
+
select :name => "Cheap - #{p.name}" , :new_price => p.price + 10
|
79
|
+
}
|
80
|
+
|
81
|
+
cheap_product_names.collect(&:name).should == ["Cheap - shoes","Cheap - pencil"]
|
82
|
+
cheap_product_names.collect(&:new_price).should == [11.75, 15.20]
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
context "group by" do
|
87
|
+
it "simple-1" do
|
88
|
+
numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ]
|
89
|
+
number_groups = __{
|
90
|
+
from n
|
91
|
+
in_ numbers
|
92
|
+
where n > 3 && n < 100
|
93
|
+
group_by n % 5 => g
|
94
|
+
select :remainder => g.key , :values => g.values
|
95
|
+
}
|
96
|
+
|
97
|
+
number_groups.collect(&:remainder).should == [0, 4, 3, 1, 2]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'linqr'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'providers/groupon/groupon_provider'
|
4
|
+
require 'awesome_print'
|
5
|
+
|
6
|
+
describe 'Groupon provider' do
|
7
|
+
|
8
|
+
it "should query groupon" do
|
9
|
+
output = _{
|
10
|
+
from deal
|
11
|
+
in_ GrouponDeals
|
12
|
+
where deal.lat == 38.8339 && deal.lng == -104.821
|
13
|
+
select deal
|
14
|
+
}
|
15
|
+
ap output.inspect
|
16
|
+
end
|
17
|
+
end
|
data/spec/order.rb
ADDED
data/spec/schema.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
ActiveRecord::Schema.define :version => 0 do
|
2
|
+
create_table "orders", :force => true do |t|
|
3
|
+
t.column :name, :string
|
4
|
+
t.timestamps
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
create_table "sub_orders", :force => true do |t|
|
9
|
+
t.column :order_id, :integer
|
10
|
+
t.column :name, :string
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table "order_items", :force => true do |t|
|
15
|
+
t.column :sub_order_id, :integer
|
16
|
+
t.column :name, :string
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linqr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- surya
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-09-10 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: bundler
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.0
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.6.4
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.4.0
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
description: Linq like sytax for querying multiple-datasources
|
50
|
+
email: surya.gaddipati@gmail.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files:
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- README.rdoc
|
61
|
+
- Rakefile
|
62
|
+
- VERSION
|
63
|
+
- examples/linqr2enumerable/ordering_operators_spec.rb
|
64
|
+
- examples/linqr2enumerable/projection_operators_spec.rb
|
65
|
+
- lib/expression_evaluator_base.rb
|
66
|
+
- lib/group_by.rb
|
67
|
+
- lib/lazy_enumerator.rb
|
68
|
+
- lib/linqr.rb
|
69
|
+
- lib/linqr_exp.rb
|
70
|
+
- lib/providers/active_record/active_record_expression_evaluator.rb
|
71
|
+
- lib/providers/active_record/activerecord_provider.rb
|
72
|
+
- lib/providers/enumerable_expression_evaluator.rb
|
73
|
+
- lib/providers/enumerable_provider.rb
|
74
|
+
- lib/providers/groupon/groupon_provider.rb
|
75
|
+
- lib/providers/hash_provider.rb
|
76
|
+
- lib/source_name_evaluator.rb
|
77
|
+
- spec/active_record_provider_spec.rb
|
78
|
+
- spec/database.yml
|
79
|
+
- spec/enumerable_expression_evaluator_spec.rb
|
80
|
+
- spec/enumerable_provider_spec.rb
|
81
|
+
- spec/groupon_provider_spec.rb
|
82
|
+
- spec/order.rb
|
83
|
+
- spec/schema.rb
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://github.com/suryagaddipati/linqr
|
87
|
+
licenses:
|
88
|
+
- MIT
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: -4136635536432510828
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: "0"
|
109
|
+
requirements: []
|
110
|
+
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.6.1
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Query Comprehensions for ruby
|
116
|
+
test_files: []
|
117
|
+
|