piglet 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +24 -4
- data/lib/piglet/field/binary_conditional.rb +15 -0
- data/lib/piglet/field/call_expression.rb +21 -0
- data/lib/piglet/field/infix_expression.rb +19 -0
- data/lib/piglet/field/literal.rb +20 -0
- data/lib/piglet/field/operators.rb +80 -0
- data/lib/piglet/field/prefix_expression.rb +23 -0
- data/lib/piglet/field/reference.rb +41 -0
- data/lib/piglet/field/rename.rb +13 -0
- data/lib/piglet/field/suffix_expression.rb +19 -0
- data/lib/piglet/inout/describe.rb +7 -0
- data/lib/piglet/inout/dump.rb +7 -0
- data/lib/piglet/inout/explain.rb +15 -0
- data/lib/piglet/inout/illustrate.rb +7 -0
- data/lib/piglet/inout/load.rb +31 -0
- data/lib/piglet/inout/output.rb +15 -0
- data/lib/piglet/inout/storage_types.rb +18 -0
- data/lib/piglet/inout/store.rb +19 -0
- data/lib/piglet/interpreter.rb +39 -7
- data/lib/piglet/relation/cogroup.rb +33 -0
- data/lib/piglet/relation/cross.rb +24 -0
- data/lib/piglet/relation/distinct.rb +18 -0
- data/lib/piglet/relation/filter.rb +15 -0
- data/lib/piglet/relation/foreach.rb +21 -0
- data/lib/piglet/relation/group.rb +23 -0
- data/lib/piglet/relation/join.rb +22 -0
- data/lib/piglet/relation/limit.rb +15 -0
- data/lib/piglet/relation/order.rb +31 -0
- data/lib/piglet/relation/relation.rb +179 -0
- data/lib/piglet/relation/sample.rb +15 -0
- data/lib/piglet/relation/split.rb +45 -0
- data/lib/piglet/relation/stream.rb +7 -0
- data/lib/piglet/relation/union.rb +21 -0
- data/lib/piglet.rb +40 -38
- data/spec/piglet/{field_spec.rb → field/reference_spec.rb} +22 -6
- data/spec/piglet/interpreter_spec.rb +51 -5
- data/spec/piglet/{relation_spec.rb → relation/relation_spec.rb} +6 -6
- data/spec/piglet/{split_spec.rb → relation/split_spec.rb} +8 -8
- data/spec/spec_helper.rb +0 -2
- metadata +39 -40
- data/examples/spike1.rb +0 -43
- data/examples/spike2.rb +0 -40
- data/lib/piglet/assignment.rb +0 -13
- data/lib/piglet/cogroup.rb +0 -31
- data/lib/piglet/cross.rb +0 -22
- data/lib/piglet/describe.rb +0 -5
- data/lib/piglet/distinct.rb +0 -16
- data/lib/piglet/dump.rb +0 -5
- data/lib/piglet/explain.rb +0 -13
- data/lib/piglet/field.rb +0 -40
- data/lib/piglet/field_expression_functions.rb +0 -62
- data/lib/piglet/field_function_expression.rb +0 -19
- data/lib/piglet/field_infix_expression.rb +0 -17
- data/lib/piglet/field_prefix_expression.rb +0 -21
- data/lib/piglet/field_rename.rb +0 -11
- data/lib/piglet/field_suffix_expression.rb +0 -17
- data/lib/piglet/filter.rb +0 -13
- data/lib/piglet/foreach.rb +0 -19
- data/lib/piglet/group.rb +0 -21
- data/lib/piglet/illustrate.rb +0 -5
- data/lib/piglet/join.rb +0 -20
- data/lib/piglet/limit.rb +0 -13
- data/lib/piglet/load.rb +0 -31
- data/lib/piglet/load_and_store.rb +0 -16
- data/lib/piglet/order.rb +0 -29
- data/lib/piglet/relation.rb +0 -177
- data/lib/piglet/sample.rb +0 -13
- data/lib/piglet/split.rb +0 -41
- data/lib/piglet/store.rb +0 -17
- data/lib/piglet/storing.rb +0 -13
- data/lib/piglet/stream.rb +0 -5
- data/lib/piglet/union.rb +0 -19
@@ -0,0 +1,22 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Join # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, description)
|
7
|
+
@join_fields = Hash[*description.select { |k, v| k.is_a?(Relation) }.flatten]
|
8
|
+
@sources = @join_fields.keys
|
9
|
+
@using = description[:using]
|
10
|
+
@parallel = description[:parallel]
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
joins = @sources.map { |s| "#{s.alias} BY #{@join_fields[s]}" }.join(', ')
|
15
|
+
str = "JOIN #{joins}"
|
16
|
+
str << " USING \"#{@using.to_s}\"" if @using
|
17
|
+
str << " PARALLEL #{@parallel}" if @parallel
|
18
|
+
str
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Order # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, fields, options)
|
7
|
+
options ||= {}
|
8
|
+
@sources, @parallel = [relation], options[:parallel]
|
9
|
+
@fields = fields.is_a?(Enumerable) ? fields : [fields]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"ORDER #{@sources.first.alias} BY #{field_strings}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def field_strings
|
19
|
+
@fields.map { |f| field_string(f) }.join(', ')
|
20
|
+
end
|
21
|
+
|
22
|
+
def field_string(f)
|
23
|
+
if f.is_a?(Enumerable)
|
24
|
+
"#{f[0]} #{f[1].to_s.upcase}"
|
25
|
+
else
|
26
|
+
f.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
module Relation
|
4
|
+
attr_reader :sources
|
5
|
+
|
6
|
+
# The name this relation will get in Pig Latin. Then name is generated when
|
7
|
+
# the relation is outputed by the interpreter, and will be unique.
|
8
|
+
def alias
|
9
|
+
@alias ||= Relation.next_alias
|
10
|
+
end
|
11
|
+
|
12
|
+
# GROUP
|
13
|
+
#
|
14
|
+
# x.group(:a) # => GROUP x By a
|
15
|
+
# x.group(:a, :b, :c) # => GROUP x BY (a, b, c)
|
16
|
+
# x.group([:a, :b, :c], :parallel => 3) # => GROUP x BY (a, b, c) PARALLEL 3
|
17
|
+
def group(*args)
|
18
|
+
grouping, options = split_at_options(args)
|
19
|
+
Group.new(self, [grouping].flatten, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# DISTINCT
|
23
|
+
#
|
24
|
+
# x.distinct # => DISTINCT x
|
25
|
+
# x.distinct(:parallel => 5) # => DISTINCT x PARALLEL 5
|
26
|
+
def distinct(options={})
|
27
|
+
Distinct.new(self, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# COGROUP
|
31
|
+
#
|
32
|
+
# x.cogroup(x => :a, y => :b) # => COGROUP x BY a, y BY b
|
33
|
+
# x.cogroup(x => :a, y => :b, z => :c) # => COGROUP x BY a, y BY b, z BY c
|
34
|
+
# x.cogroup(x => [:a, :b], y => [:c, :d]) # => COGROUP x BY (a, b), y BY (c, d)
|
35
|
+
# x.cogroup(x => :a, y => [:b, :inner]) # => COGROUP x BY a, y BY b INNER
|
36
|
+
# x.cogroup(x => :a, y => :b, :parallel => 5) # => COGROUP x BY a, y BY b PARALLEL 5
|
37
|
+
def cogroup(description)
|
38
|
+
Cogroup.new(self, description)
|
39
|
+
end
|
40
|
+
|
41
|
+
# CROSS
|
42
|
+
#
|
43
|
+
# x.cross(y) # => CROSS x, y
|
44
|
+
# x.cross(y, z, w) # => CROSS x, y, z, w
|
45
|
+
# x.cross([y, z], :parallel => 5) # => CROSS x, y, z, w PARALLEL 5
|
46
|
+
def cross(*args)
|
47
|
+
relations, options = split_at_options(args)
|
48
|
+
Cross.new(([self] + relations).flatten, options)
|
49
|
+
end
|
50
|
+
|
51
|
+
# FILTER
|
52
|
+
#
|
53
|
+
# x.filter { |r| r.a == r.b } # => FILTER x BY a == b
|
54
|
+
# x.filter { |r| r.a > r.b && r.c != 3 } # => FILTER x BY a > b AND c != 3
|
55
|
+
def filter
|
56
|
+
Filter.new(self, yield(self))
|
57
|
+
end
|
58
|
+
|
59
|
+
# FOREACH ... GENERATE
|
60
|
+
#
|
61
|
+
# x.foreach { |r| r.a } # => FOREACH x GENERATE a
|
62
|
+
# x.foreach { |r| [r.a, r.b] } # => FOREACH x GENERATE a, b
|
63
|
+
# x.foreach { |r| r.a.max } # => FOREACH x GENERATE MAX(a)
|
64
|
+
# x.foreach { |r| r.a.avg.as(:b) } # => FOREACH x GENERATE AVG(a) AS b
|
65
|
+
#
|
66
|
+
#--
|
67
|
+
#
|
68
|
+
# TODO: FOREACH a { b GENERATE c }
|
69
|
+
def foreach
|
70
|
+
Foreach.new(self, yield(self))
|
71
|
+
end
|
72
|
+
|
73
|
+
# JOIN
|
74
|
+
#
|
75
|
+
# x.join(x => :a, y => :b) # => JOIN x BY a, y BY b
|
76
|
+
# x.join(x => :a, y => :b, z => :c) # => JOIN x BY a, y BY b, z BY c
|
77
|
+
# x.join(x => :a, y => :b, :using => :replicated) # => JOIN x BY a, y BY b USING "replicated"
|
78
|
+
# x.join(x => :a, y => :b, :parallel => 5) # => JOIN x BY a, y BY b PARALLEL 5
|
79
|
+
def join(description)
|
80
|
+
Join.new(self, description)
|
81
|
+
end
|
82
|
+
|
83
|
+
# LIMIT
|
84
|
+
#
|
85
|
+
# x.limit(10) # => LIMIT x 10
|
86
|
+
def limit(n)
|
87
|
+
Limit.new(self, n)
|
88
|
+
end
|
89
|
+
|
90
|
+
# ORDER
|
91
|
+
#
|
92
|
+
# x.order(:a) # => ORDER x BY a
|
93
|
+
# x.order(:a, :b) # => ORDER x BY a, b
|
94
|
+
# x.order([:a, :asc], [:b, :desc]) # => ORDER x BY a ASC, b DESC
|
95
|
+
# x.order(:a, :parallel => 5) # => ORDER x BY a PARALLEL 5
|
96
|
+
#
|
97
|
+
#--
|
98
|
+
#
|
99
|
+
# NOTE: the syntax x.order(:a => :asc, :b => :desc) would be nice, but in
|
100
|
+
# Ruby 1.8 the order of the keys cannot be guaranteed.
|
101
|
+
def order(*args)
|
102
|
+
fields, options = split_at_options(args)
|
103
|
+
fields = *fields
|
104
|
+
Order.new(self, fields, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
# SAMPLE
|
108
|
+
#
|
109
|
+
# x.sample(5) # => SAMPLE x 5;
|
110
|
+
def sample(n)
|
111
|
+
Sample.new(self, n)
|
112
|
+
end
|
113
|
+
|
114
|
+
# SPLIT
|
115
|
+
#
|
116
|
+
# y, z = x.split { |r| [r.a <= 3, r.b > 4]} # => SPLIT x INTO y IF a <= 3, z IF a > 4
|
117
|
+
def split
|
118
|
+
Split.new(self, yield(self)).shards
|
119
|
+
end
|
120
|
+
|
121
|
+
# STREAM
|
122
|
+
#
|
123
|
+
# x.stream(x, 'cut -f 3') # => STREAM x THROUGH `cut -f 3`
|
124
|
+
# x.stream([x, y], 'cut -f 3') # => STREAM x, y THROUGH `cut -f 3`
|
125
|
+
# x.stream(x, 'cut -f 3', :schema => [%w(a int)]) # => STREAM x THROUGH `cut -f 3` AS (a:int)
|
126
|
+
#
|
127
|
+
#--
|
128
|
+
#
|
129
|
+
# TODO: how to handle DEFINE'd commands?
|
130
|
+
def stream(relations, command, options={})
|
131
|
+
raise NotSupportedError
|
132
|
+
end
|
133
|
+
|
134
|
+
# UNION
|
135
|
+
#
|
136
|
+
# x.union(y) # => UNION x, y
|
137
|
+
# x.union(y, z) # => UNION x, y, z
|
138
|
+
def union(*relations)
|
139
|
+
Union.new(*([self] + relations))
|
140
|
+
end
|
141
|
+
|
142
|
+
def method_missing(name, *args)
|
143
|
+
if name.to_s =~ /^\w+$/ && args.empty?
|
144
|
+
Field::Reference.new(name, self)
|
145
|
+
else
|
146
|
+
super
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def [](n)
|
151
|
+
Field::Reference.new("\$#{n}", self)
|
152
|
+
end
|
153
|
+
|
154
|
+
def hash
|
155
|
+
self.alias.hash
|
156
|
+
end
|
157
|
+
|
158
|
+
def eql?(other)
|
159
|
+
other.is_a?(Relation) && other.alias == self.alias
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def split_at_options(parameters)
|
165
|
+
if parameters.last.is_a? Hash
|
166
|
+
[parameters[0..-2], parameters.last]
|
167
|
+
else
|
168
|
+
[parameters, nil]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.next_alias
|
173
|
+
@counter ||= 0
|
174
|
+
@counter += 1
|
175
|
+
"relation_#{@counter}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Split # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, expressions)
|
7
|
+
@sources, @expressions = [relation], expressions
|
8
|
+
@shard_map = create_shards
|
9
|
+
end
|
10
|
+
|
11
|
+
def shards
|
12
|
+
@shard_map.values_at(*@expressions)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
split_strings = @expressions.map do |expression|
|
17
|
+
"#{@shard_map[expression].alias} IF #{expression}"
|
18
|
+
end
|
19
|
+
|
20
|
+
"SPLIT #{@sources.first.alias} INTO #{split_strings.join(', ')}"
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def create_shards
|
26
|
+
@expressions.inject({}) do |map, expr|
|
27
|
+
map[expr] = RelationShard.new(self)
|
28
|
+
map
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class RelationShard # :nodoc:
|
34
|
+
include Relation
|
35
|
+
|
36
|
+
def initialize(split)
|
37
|
+
@sources = [split]
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
self.alias
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Union # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(*relations)
|
7
|
+
@sources = relations
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"UNION #{source_aliases.join(', ')}"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def source_aliases
|
17
|
+
@sources.map { |s| s.alias }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/piglet.rb
CHANGED
@@ -1,45 +1,47 @@
|
|
1
1
|
# :main: README.rdoc
|
2
2
|
module Piglet # :nodoc:
|
3
|
-
VERSION = '0.1.
|
3
|
+
VERSION = '0.1.2'
|
4
4
|
|
5
|
-
|
6
|
-
assignment
|
7
|
-
cogroup
|
8
|
-
cross
|
9
|
-
describe
|
10
|
-
distinct
|
11
|
-
dump
|
12
|
-
explain
|
13
|
-
field
|
14
|
-
field_expression_functions
|
15
|
-
field_function_expression
|
16
|
-
field_infix_expression
|
17
|
-
field_prefix_expression
|
18
|
-
field_rename
|
19
|
-
field_suffix_expression
|
20
|
-
filter
|
21
|
-
foreach
|
22
|
-
group
|
23
|
-
illustrate
|
24
|
-
interpreter
|
25
|
-
join
|
26
|
-
limit
|
27
|
-
load
|
28
|
-
load_and_store
|
29
|
-
order
|
30
|
-
relation
|
31
|
-
sample
|
32
|
-
split
|
33
|
-
store
|
34
|
-
storing
|
35
|
-
stream
|
36
|
-
union
|
37
|
-
)
|
5
|
+
autoload :Interpreter, 'piglet/interpreter'
|
38
6
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
autoload
|
7
|
+
module Inout
|
8
|
+
autoload :Describe, 'piglet/inout/describe'
|
9
|
+
autoload :Dump, 'piglet/inout/dump'
|
10
|
+
autoload :Explain, 'piglet/inout/explain'
|
11
|
+
autoload :Illustrate, 'piglet/inout/illustrate'
|
12
|
+
autoload :Load, 'piglet/inout/load'
|
13
|
+
autoload :Output, 'piglet/inout/output'
|
14
|
+
autoload :StorageTypes, 'piglet/inout/storage_types'
|
15
|
+
autoload :Store, 'piglet/inout/store'
|
16
|
+
end
|
17
|
+
|
18
|
+
module Relation
|
19
|
+
autoload :Cogroup, 'piglet/relation/cogroup'
|
20
|
+
autoload :Cross, 'piglet/relation/cross'
|
21
|
+
autoload :Distinct, 'piglet/relation/distinct'
|
22
|
+
autoload :Filter, 'piglet/relation/filter'
|
23
|
+
autoload :Foreach, 'piglet/relation/foreach'
|
24
|
+
autoload :Group, 'piglet/relation/group'
|
25
|
+
autoload :Join, 'piglet/relation/join'
|
26
|
+
autoload :Limit, 'piglet/relation/limit'
|
27
|
+
autoload :Order, 'piglet/relation/order'
|
28
|
+
autoload :Relation, 'piglet/relation/relation'
|
29
|
+
autoload :Sample, 'piglet/relation/sample'
|
30
|
+
autoload :Split, 'piglet/relation/split'
|
31
|
+
autoload :Stream, 'piglet/relation/stream'
|
32
|
+
autoload :Union, 'piglet/relation/union'
|
33
|
+
end
|
34
|
+
|
35
|
+
module Field
|
36
|
+
autoload :BinaryConditional, 'piglet/field/binary_conditional'
|
37
|
+
autoload :CallExpression, 'piglet/field/call_expression'
|
38
|
+
autoload :InfixExpression, 'piglet/field/infix_expression'
|
39
|
+
autoload :Literal, 'piglet/field/literal'
|
40
|
+
autoload :Operators, 'piglet/field/operators'
|
41
|
+
autoload :PrefixExpression, 'piglet/field/prefix_expression'
|
42
|
+
autoload :Reference, 'piglet/field/reference'
|
43
|
+
autoload :Rename, 'piglet/field/rename'
|
44
|
+
autoload :SuffixExpression, 'piglet/field/suffix_expression'
|
43
45
|
end
|
44
46
|
|
45
47
|
class NotSupportedError < StandardError; end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
2
|
|
3
3
|
|
4
|
-
describe Piglet::Field do
|
4
|
+
describe Piglet::Field::Reference do
|
5
5
|
|
6
6
|
before do
|
7
|
-
@field = Piglet::Field.new('field')
|
7
|
+
@field = Piglet::Field::Reference.new('field')
|
8
8
|
end
|
9
9
|
|
10
10
|
describe '#to_s' do
|
@@ -13,7 +13,7 @@ describe Piglet::Field do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'returns a string with the field name (as a symbol)' do
|
16
|
-
@field = Piglet::Field.new(:field)
|
16
|
+
@field = Piglet::Field::Reference.new(:field)
|
17
17
|
@field.to_s.should eql("field")
|
18
18
|
end
|
19
19
|
end
|
@@ -52,8 +52,8 @@ describe Piglet::Field do
|
|
52
52
|
|
53
53
|
context 'infix and unary operators' do
|
54
54
|
before do
|
55
|
-
@field1 = Piglet::Field.new('field1')
|
56
|
-
@field2 = Piglet::Field.new('field2')
|
55
|
+
@field1 = Piglet::Field::Reference.new('field1')
|
56
|
+
@field2 = Piglet::Field::Reference.new('field2')
|
57
57
|
end
|
58
58
|
|
59
59
|
[:==, :>, :<, :>=, :<=, :%, :+, :-, :*, :/].each do |op|
|
@@ -125,6 +125,22 @@ describe Piglet::Field do
|
|
125
125
|
it 'supports casts on an expression' do
|
126
126
|
(@field1 + @field2).cast(:chararray).to_s.should eql("(chararray) (field1 + field2)")
|
127
127
|
end
|
128
|
+
|
129
|
+
it 'supports AND though #and on fields' do
|
130
|
+
@field1.and(@field2).to_s.should eql("field1 AND field2")
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'supports AND through #and on expressions' do
|
134
|
+
(@field1 + @field2).and(@field1 - @field2).to_s.should eql("(field1 + field2) AND (field1 - field2)")
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'supports OR though #or on fields' do
|
138
|
+
@field1.or(@field2).to_s.should eql("field1 OR field2")
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'supports OR through #or on expressions' do
|
142
|
+
(@field1 + @field2).or(@field1 - @field2).to_s.should eql("(field1 + field2) OR (field1 - field2)")
|
143
|
+
end
|
128
144
|
end
|
129
145
|
|
130
146
|
end
|
@@ -40,7 +40,7 @@ describe Piglet::Interpreter do
|
|
40
40
|
@interpreter.to_pig_latin.should include("LOAD 'some/path' USING XYZ;")
|
41
41
|
end
|
42
42
|
|
43
|
-
Piglet::
|
43
|
+
Piglet::Inout::StorageTypes::LOAD_STORE_FUNCTIONS.each do |symbolic_name, function|
|
44
44
|
it "knows that the load method :#{symbolic_name} means #{function}" do
|
45
45
|
@interpreter.interpret { store(load('some/path', :using => symbolic_name), 'out') }
|
46
46
|
@interpreter.to_pig_latin.should include("LOAD 'some/path' USING #{function};")
|
@@ -267,10 +267,10 @@ describe Piglet::Interpreter do
|
|
267
267
|
@interpreter.to_pig_latin.should match(/FILTER \w+ BY a == 3/)
|
268
268
|
end
|
269
269
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
270
|
+
it 'outputs a FILTER statement with a complex test' do
|
271
|
+
@interpreter.interpret { dump(load('in').filter { |r| (r.a > r.b).and(r.c.ne(3)) }) }
|
272
|
+
@interpreter.to_pig_latin.should match(/FILTER \w+ BY \(a > b\) AND \(c != 3\)/)
|
273
|
+
end
|
274
274
|
end
|
275
275
|
|
276
276
|
describe 'SPLIT' do
|
@@ -410,4 +410,50 @@ describe Piglet::Interpreter do
|
|
410
410
|
end
|
411
411
|
end
|
412
412
|
|
413
|
+
context 'misc. operators' do
|
414
|
+
it 'outputs a binary conditional when using #test' do
|
415
|
+
@interpreter.interpret do
|
416
|
+
dump(load('in').foreach { |r| [test(r.a == r.b, r.a, r.b)]})
|
417
|
+
end
|
418
|
+
@interpreter.to_pig_latin.should include('(a == b ? a : b)')
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context 'literals' do
|
423
|
+
it 'outputs a literal string when passing a string to #literal' do
|
424
|
+
@interpreter.interpret do
|
425
|
+
dump(load('in').foreach { |r| [literal('hello').as(:world)]})
|
426
|
+
end
|
427
|
+
@interpreter.to_pig_latin.should include("'hello' AS world")
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'outputs a literal integer when passing an integer to #literal' do
|
431
|
+
@interpreter.interpret do
|
432
|
+
dump(load('in').foreach { |r| [literal(3).as(:n)]})
|
433
|
+
end
|
434
|
+
@interpreter.to_pig_latin.should include("3 AS n")
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'outputs a literal float when passing a float to #literal' do
|
438
|
+
@interpreter.interpret do
|
439
|
+
dump(load('in').foreach { |r| [literal(3.14).as(:pi)]})
|
440
|
+
end
|
441
|
+
@interpreter.to_pig_latin.should include("3.14 AS pi")
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'outputs a literal string when passing an arbitrary object to #literal' do
|
445
|
+
@interpreter.interpret do
|
446
|
+
dump(load('in').foreach { |r| [literal(self).as(:interpreter)]})
|
447
|
+
end
|
448
|
+
@interpreter.to_pig_latin.should match(/'[^']+' AS interpreter/)
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'escapes single quotes in literal strings' do
|
452
|
+
@interpreter.interpret do
|
453
|
+
dump(load('in').foreach { |r| [literal("hello 'world'").as(:str)]})
|
454
|
+
end
|
455
|
+
@interpreter.to_pig_latin.should include("'hello \\'world\\'' AS str")
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
413
459
|
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
2
|
|
3
3
|
|
4
|
-
describe Piglet::Relation do
|
4
|
+
describe Piglet::Relation::Relation do
|
5
5
|
|
6
6
|
before do
|
7
7
|
@relation = Object.new
|
8
|
-
@relation.extend Piglet::Relation
|
8
|
+
@relation.extend Piglet::Relation::Relation
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'has a alias' do
|
@@ -16,7 +16,7 @@ describe Piglet::Relation do
|
|
16
16
|
aliases = { }
|
17
17
|
1000.times do
|
18
18
|
@relation = Object.new
|
19
|
-
@relation.extend Piglet::Relation
|
19
|
+
@relation.extend Piglet::Relation::Relation
|
20
20
|
aliases.should_not have_key(@relation.alias)
|
21
21
|
aliases[@relation.alias] = @relation
|
22
22
|
end
|
@@ -37,7 +37,7 @@ describe Piglet::Relation do
|
|
37
37
|
describe '#cross' do
|
38
38
|
it 'returns a new relation with the target relation as one of the sources' do
|
39
39
|
other = Object.new
|
40
|
-
other.extend Piglet::Relation
|
40
|
+
other.extend Piglet::Relation::Relation
|
41
41
|
@relation.cross(other).sources.should include(@relation)
|
42
42
|
end
|
43
43
|
end
|
@@ -45,7 +45,7 @@ describe Piglet::Relation do
|
|
45
45
|
describe '#union' do
|
46
46
|
it 'returns a new relation with the target relation as one of the sources' do
|
47
47
|
other = Object.new
|
48
|
-
other.extend Piglet::Relation
|
48
|
+
other.extend Piglet::Relation::Relation
|
49
49
|
@relation.union(other).sources.should include(@relation)
|
50
50
|
end
|
51
51
|
end
|
@@ -1,27 +1,27 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
2
|
|
3
3
|
|
4
|
-
describe Piglet::Split do
|
4
|
+
describe Piglet::Relation::Split do
|
5
5
|
|
6
6
|
before do
|
7
7
|
@relation = mock('source')
|
8
8
|
@expr1 = mock('expr1')
|
9
9
|
@expr2 = mock('expr2')
|
10
10
|
@relation.stub!(:alias).and_return('rel')
|
11
|
-
@expr1.stub!(:to_s).and_return('
|
12
|
-
@expr2.stub!(:to_s).and_return('
|
13
|
-
@split = Piglet::Split.new(@relation, [@expr1, @expr2])
|
11
|
+
@expr1.stub!(:to_s).and_return('expr1')
|
12
|
+
@expr2.stub!(:to_s).and_return('expr2')
|
13
|
+
@split = Piglet::Relation::Split.new(@relation, [@expr1, @expr2])
|
14
14
|
end
|
15
15
|
|
16
16
|
describe '#to_s' do
|
17
17
|
it 'outputs all x IF y expressions' do
|
18
|
-
@split.to_s.should match(/SPLIT rel INTO \w+ IF [
|
18
|
+
@split.to_s.should match(/SPLIT rel INTO \w+ IF expr[12], \w+ IF expr[12]/)
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'contains the names of all the shard relations' do
|
22
22
|
@shards = @split.shards
|
23
|
-
@split.to_s.should include("#{@shards[0].alias} IF
|
24
|
-
@split.to_s.should include("#{@shards[1].alias} IF
|
23
|
+
@split.to_s.should include("#{@shards[0].alias} IF expr1")
|
24
|
+
@split.to_s.should include("#{@shards[1].alias} IF expr2")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|