linqr 0.1.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/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
|
+
|