alf 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/CHANGELOG.md +64 -0
  2. data/Gemfile.lock +4 -4
  3. data/README.md +257 -171
  4. data/TODO.md +4 -4
  5. data/alf.gemspec +3 -3
  6. data/alf.noespec +11 -6
  7. data/examples/pseudo-with.alf +7 -0
  8. data/examples/runall.sh +2 -2
  9. data/examples/unwrap.alf +4 -0
  10. data/examples/wrap.alf +2 -0
  11. data/lib/alf/relation.rb +118 -0
  12. data/lib/alf/version.rb +1 -1
  13. data/lib/alf.rb +320 -169
  14. data/spec/integration/src/test_minus.alf +5 -0
  15. data/spec/integration/src/test_project.alf +9 -0
  16. data/spec/{alf_spec.rb → integration/test_alf.rb} +8 -21
  17. data/spec/integration/test_alf_specs.rb +37 -0
  18. data/spec/{examples_spec.rb → integration/test_examples.rb} +1 -1
  19. data/spec/spec_helper.rb +19 -1
  20. data/spec/unit/environment/examples/suppliers.rash +5 -0
  21. data/spec/{environment/explicit_spec.rb → unit/environment/test_explicit.rb} +0 -0
  22. data/spec/{environment/folder_spec.rb → unit/environment/test_folder.rb} +1 -1
  23. data/spec/{operator → unit/operator}/non_relational/compact/buffer_based.rb +0 -0
  24. data/spec/{operator/non_relational/compact/sort_based_spec.rb → unit/operator/non_relational/compact/test_sort_based.rb} +0 -0
  25. data/spec/{operator/non_relational/autonum_spec.rb → unit/operator/non_relational/test_autonum.rb} +0 -0
  26. data/spec/{operator/non_relational/clip_spec.rb → unit/operator/non_relational/test_clip.rb} +0 -0
  27. data/spec/{operator/non_relational/compact_spec.rb → unit/operator/non_relational/test_compact.rb} +0 -0
  28. data/spec/{operator/non_relational/defaults_spec.rb → unit/operator/non_relational/test_defaults.rb} +0 -0
  29. data/spec/{operator/non_relational/sort_spec.rb → unit/operator/non_relational/test_sort.rb} +0 -0
  30. data/spec/{operator/relational/join/hash_based_spec.rb → unit/operator/relational/join/test_hash_based.rb} +0 -0
  31. data/spec/unit/operator/relational/summarize/test_hash_based.rb +38 -0
  32. data/spec/{operator/relational/summarize/sort_based_spec.rb → unit/operator/relational/summarize/test_sort_based.rb} +0 -0
  33. data/spec/{operator/relational/extend_spec.rb → unit/operator/relational/test_extend.rb} +0 -0
  34. data/spec/{operator/relational/group_spec.rb → unit/operator/relational/test_group.rb} +3 -2
  35. data/spec/{operator/relational/intersect_spec.rb → unit/operator/relational/test_intersect.rb} +0 -0
  36. data/spec/unit/operator/relational/test_join.rb +36 -0
  37. data/spec/{operator/relational/minus_spec.rb → unit/operator/relational/test_minus.rb} +0 -0
  38. data/spec/{operator/relational/project_spec.rb → unit/operator/relational/test_project.rb} +0 -0
  39. data/spec/{operator/relational/quota_spec.rb → unit/operator/relational/test_quota.rb} +0 -0
  40. data/spec/{operator/relational/rename_spec.rb → unit/operator/relational/test_rename.rb} +0 -0
  41. data/spec/{operator/relational/restrict_spec.rb → unit/operator/relational/test_restrict.rb} +0 -0
  42. data/spec/unit/operator/relational/test_summarize.rb +64 -0
  43. data/spec/{operator/relational/ungroup_spec.rb → unit/operator/relational/test_ungroup.rb} +0 -0
  44. data/spec/{operator/relational/union_spec.rb → unit/operator/relational/test_union.rb} +0 -0
  45. data/spec/{operator/relational/unnest_spec.rb → unit/operator/relational/test_unwrap.rb} +5 -5
  46. data/spec/{operator/relational/nest_spec.rb → unit/operator/relational/test_wrap.rb} +5 -5
  47. data/spec/{operator/command_methods_spec.rb → unit/operator/test_command_methods.rb} +0 -0
  48. data/spec/unit/operator/test_non_relational.rb +18 -0
  49. data/spec/unit/operator/test_relational.rb +27 -0
  50. data/spec/{reader → unit/reader}/input.rb +0 -0
  51. data/spec/unit/reader/test_alf_file.rb +27 -0
  52. data/spec/{reader/rash_spec.rb → unit/reader/test_rash.rb} +0 -0
  53. data/spec/unit/relation/test_coerce.rb +53 -0
  54. data/spec/unit/relation/test_inspect.rb +20 -0
  55. data/spec/unit/relation/test_relops.rb +46 -0
  56. data/spec/{renderer/text/cell_spec.rb → unit/renderer/text/test_cell.rb} +0 -0
  57. data/spec/{renderer/text/row_spec.rb → unit/renderer/text/test_row.rb} +0 -0
  58. data/spec/{renderer/text/table_spec.rb → unit/renderer/text/test_table.rb} +0 -0
  59. data/spec/{aggregator_spec.rb → unit/test_aggregator.rb} +6 -6
  60. data/spec/{assumptions_spec.rb → unit/test_assumptions.rb} +0 -0
  61. data/spec/{lispy_spec.rb → unit/test_lispy.rb} +0 -0
  62. data/spec/unit/test_operator.rb +16 -0
  63. data/spec/{reader_spec.rb → unit/test_reader.rb} +4 -0
  64. data/spec/unit/test_relation.rb +40 -0
  65. data/spec/{renderer_spec.rb → unit/test_renderer.rb} +0 -0
  66. data/spec/{tools/ordering_key_spec.rb → unit/tools/test_ordering_key.rb} +0 -0
  67. data/spec/{tools/projection_key_spec.rb → unit/tools/test_projection_key.rb} +0 -0
  68. data/spec/{tools/tools_spec.rb → unit/tools/test_tools.rb} +0 -0
  69. data/spec/{tools/tuple_handle_spec.rb → unit/tools/test_tuple_handle.rb} +0 -0
  70. data/tasks/clean.rake +3 -0
  71. data/tasks/spec_test.rake +1 -1
  72. metadata +143 -114
  73. data/examples/nest.alf +0 -2
  74. data/examples/unnest.alf +0 -4
  75. data/examples/with.alf +0 -23
  76. data/spec/operator/relational/summarize_spec.rb +0 -41
  77. data/spec/reader/alf_file_spec.rb +0 -15
@@ -0,0 +1,5 @@
1
+ (specify \
2
+ "(minus (restrict ...) ...) should be equivalent to (restict ..., lambda{ not(...) })",
3
+ (rel_equal \
4
+ (minus :suppliers, (restrict :suppliers, lambda{ city == 'Paris' })),
5
+ (restrict :suppliers, lambda{ city != 'Paris' })))
@@ -0,0 +1,9 @@
1
+ cities = relation(
2
+ {:city => 'London'},
3
+ {:city => 'Paris'},
4
+ {:city => 'Athens'}
5
+ )
6
+ (specify \
7
+ "(project ..., [...]) should remove duplicates",
8
+ (rel_equal \
9
+ (project :suppliers, [:city]), cities))
@@ -18,30 +18,17 @@ describe Alf do
18
18
  end
19
19
 
20
20
  it "should allow compiling lispy expressions" do
21
- lispy.compile{
21
+ lispy.compile {
22
22
  (restrict :suppliers, lambda{ city == 'London'})
23
23
  }.to_a.should == expected
24
24
  end
25
-
26
- it "should allow defining temporary expressions" do
27
- lispy.compile{
28
- with(:sup => (dataset :suppliers)) do
29
- (restrict :sup, lambda{ city == 'London'})
30
- end
31
- }.to_a.should == expected
32
- end
33
25
 
34
- it "should allow reusing temporary expressions" do
35
- op = lispy.compile do
36
- (restrict :suppliers, lambda{ status > 20 })
37
- end
38
- projection = lispy.with(:kept_suppliers => op) do
39
- (project :kept_suppliers, [:city])
40
- end
41
- projection.to_a.should == [
42
- {:city => 'Paris'},
43
- {:city => 'Athens'}
44
- ]
26
+ it "should allow evaluating lispy expressions" do
27
+ rel = lispy.evaluate {
28
+ (restrict :suppliers, lambda{ city == 'London'})
29
+ }
30
+ rel.should be_a(Alf::Relation)
31
+ rel.should eq(Alf::Relation.coerce(expected))
45
32
  end
46
-
33
+
47
34
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ describe "Alf's integration tests" do
3
+
4
+ module Helpers
5
+
6
+ def rel_equal(x, y)
7
+ x.to_rel == y.to_rel
8
+ end
9
+
10
+ def specify(message, x)
11
+ raise message unless x
12
+ end
13
+
14
+ end
15
+
16
+ shared_examples_for "An integration file" do
17
+
18
+ let(:lispy){
19
+ lispy = Alf.lispy(Alf::Environment.examples)
20
+ lispy.ruby_extend(Helpers)
21
+ }
22
+
23
+ it "should work when executed with a Alf" do
24
+ lispy.compile(File.read(subject))
25
+ end
26
+
27
+ end # An example
28
+
29
+
30
+ Dir["#{File.expand_path('../src', __FILE__)}/**/*.alf"].each do |file|
31
+ describe file do
32
+ subject{ file }
33
+ it_should_behave_like "An integration file"
34
+ end
35
+ end
36
+
37
+ end
@@ -15,7 +15,7 @@ module Alf
15
15
  end # An example
16
16
 
17
17
 
18
- Dir["#{File.expand_path('../../examples', __FILE__)}/**/*.alf"].each do |file|
18
+ Dir["#{File.expand_path('../../../examples', __FILE__)}/**/*.alf"].each do |file|
19
19
  describe file do
20
20
  subject{ file }
21
21
  it_should_behave_like "An example"
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,12 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
1
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
2
  require 'alf'
3
3
 
4
4
  Alf::Lispy.extend(Alf::Lispy)
5
5
 
6
+ def rel(*args)
7
+ Alf::Relation.coerce(args)
8
+ end
9
+
6
10
  shared_examples_for "An operator class" do
7
11
 
8
12
  it "should not have public set_args, _each and _prepare methods" do
@@ -22,5 +26,19 @@ shared_examples_for "An operator class" do
22
26
  it "should have a public each method" do
23
27
  operator_class.public_method_defined?(:each).should be_true
24
28
  end
29
+
30
+ it "should have a unary? class method" do
31
+ operator_class.should respond_to(:unary?)
32
+ end
33
+
34
+ it "should have a binary? class method" do
35
+ operator_class.should respond_to(:binary?)
36
+ end
37
+
38
+ it "should implement unary? and binary? accurately" do
39
+ operator_class.unary?.should_not eq(operator_class.binary?)
40
+ operator_class.unary?.should eq(operator_class.ancestors.include?(Alf::Operator::Unary))
41
+ operator_class.binary?.should eq(operator_class.ancestors.include?(Alf::Operator::Binary))
42
+ end
25
43
 
26
44
  end
@@ -0,0 +1,5 @@
1
+ {:sid => 'S1', :name => 'Smith', :status => 20, :city => 'London'}
2
+ {:sid => 'S2', :name => 'Jones', :status => 10, :city => 'Paris'}
3
+ {:sid => 'S3', :name => 'Blake', :status => 30, :city => 'Paris'}
4
+ {:sid => 'S4', :name => 'Clark', :status => 20, :city => 'London'}
5
+ {:sid => 'S5', :name => 'Adams', :status => 30, :city => 'Athens'}
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
  module Alf
3
3
  describe Environment::Folder do
4
4
 
5
- let(:path){ File.expand_path('../../../examples', __FILE__) }
5
+ let(:path){ File.expand_path('../examples', __FILE__) }
6
6
  let(:env){ Environment::Folder.new(path) }
7
7
 
8
8
  describe "dataset" do
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ module Operator::Relational
4
+ describe Summarize::HashBased do
5
+
6
+ let(:input) {[
7
+ {:a => "via_method", :time => 1},
8
+ {:a => "via_reader", :time => 4},
9
+ {:a => "via_method", :time => 2},
10
+ {:a => "via_reader", :time => 2},
11
+ {:a => "via_method", :time => 1},
12
+ ]}
13
+
14
+ let(:expected) {[
15
+ {:a => "via_method", :time_sum => 4, :time_max => 2},
16
+ {:a => "via_reader", :time_sum => 6, :time_max => 4},
17
+ ]}
18
+
19
+ let(:aggs){{:time_sum => Aggregator.sum(:time),
20
+ :time_max => Aggregator.max(:time)}}
21
+ let(:operator){ Summarize::HashBased.new(by_key, aggs) }
22
+
23
+ before{ operator.pipe(input) }
24
+ subject{ operator.to_a.sort{|t1,t2| t1[:a] <=> t2[:a]} }
25
+
26
+ describe "when allbut is not set" do
27
+ let(:by_key){ Tools::ProjectionKey.new([:a], false) }
28
+ it { should == expected }
29
+ end
30
+
31
+ describe "when allbut is set" do
32
+ let(:by_key){ Tools::ProjectionKey.new([:time], true) }
33
+ it { should == expected }
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -13,8 +13,9 @@ module Alf
13
13
  ]}
14
14
 
15
15
  let(:expected) {[
16
- {:a => "via_method", :as => [{:time => 1, :b => "b"}, {:time => 2, :b => "b"}]},
17
- {:a => "via_reader", :as => [{:time => 3, :b => "b"}]},
16
+ {:a => "via_method", :as => rel({:time => 1, :b => "b"},
17
+ {:time => 2, :b => "b"})},
18
+ {:a => "via_reader", :as => rel({:time => 3, :b => "b"})},
18
19
  ]}
19
20
 
20
21
  subject{ operator.to_a.sort{|k1,k2| k1[:a] <=> k2[:a]} }
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ module Operator::Relational
4
+ describe Join do
5
+
6
+ let(:suppliers){Relation.coerce [
7
+ {:sid => 'S1', :city => 'London'},
8
+ {:sid => 'S2', :city => 'Paris'},
9
+ {:sid => 'S3', :city => 'Paris'},
10
+ {:sid => 'S4', :city => 'London'},
11
+ {:sid => 'S5', :city => 'Athens'},
12
+ ]}
13
+
14
+ describe "when applied to sub-relations" do
15
+ let(:suppliers_by_city){Relation.coerce(
16
+ Lispy.group(suppliers, [:sid], :suppliers)
17
+ )}
18
+ let(:s2_s3){Relation.coerce([
19
+ {:sid => 'S3'},
20
+ {:sid => 'S2'}
21
+ ])}
22
+ let(:right){Relation.coerce([
23
+ {:suppliers => s2_s3, :hello => "world"}
24
+ ])}
25
+ let(:expected){Relation.coerce([
26
+ {:suppliers => s2_s3, :hello => "world", :city => 'Paris'}
27
+ ])}
28
+ subject{Relation.coerce(
29
+ Lispy.join(suppliers_by_city, right)
30
+ )}
31
+ it{ should == expected }
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ module Operator::Relational
4
+ describe Summarize do
5
+
6
+ let(:operator_class){ Summarize }
7
+ it_should_behave_like("An operator class")
8
+
9
+ let(:input) {[
10
+ {:a => "via_reader", :time => 2},
11
+ {:a => "via_method", :time => 1},
12
+ {:a => "via_method", :time => 2},
13
+ {:a => "via_reader", :time => 4},
14
+ {:a => "via_method", :time => 1},
15
+ ]}
16
+
17
+ let(:expected) {[
18
+ {:a => "via_method", :time_sum => 4, :time_max => 2, :time_avg => 4.0/3},
19
+ {:a => "via_reader", :time_sum => 6, :time_max => 4, :time_avg => 6.0/2},
20
+ ]}
21
+
22
+ let(:aggs){{:time_sum => Aggregator.sum(:time),
23
+ :time_max => Aggregator.max(:time),
24
+ :time_avg => Aggregator.avg(:time)}}
25
+
26
+ subject{ operator.to_a.sort{|t1,t2| t1[:a] <=> t2[:a]} }
27
+
28
+ describe "without allbut" do
29
+
30
+ describe "When factored with commandline args" do
31
+ let(:opts){ ["--by=a"] }
32
+ let(:aggs){ ["time_sum", "sum(:time)", "time_max", "max(:time)", "time_avg", "avg(:time)"] }
33
+ let(:operator){ Summarize.run(opts + ["--"] +aggs) }
34
+ before{ operator.pipe(input) }
35
+ it { should == expected }
36
+ end
37
+
38
+ describe "When factored with Lispy" do
39
+ let(:operator){ Lispy.summarize(input, [:a], aggs) }
40
+ it { should == expected }
41
+ end
42
+
43
+ end
44
+
45
+ describe "with allbut" do
46
+
47
+ describe "When factored with commandline args" do
48
+ let(:opts){ ["--by=time", "--allbut"] }
49
+ let(:aggs){ ["time_sum", "sum(:time)", "time_max", "max(:time)", "time_avg", "avg(:time)"] }
50
+ let(:operator){ Summarize.run(opts + ["--"] + aggs) }
51
+ before{ operator.pipe(input) }
52
+ it { should == expected }
53
+ end
54
+
55
+ describe "When factored with Lispy" do
56
+ let(:operator){ Lispy.summarize(input, [:time], aggs, true) }
57
+ it { should == expected }
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
  module Alf
3
3
  module Operator::Relational
4
- describe Unnest do
4
+ describe Unwrap do
5
5
 
6
- let(:operator_class){ Unnest }
6
+ let(:operator_class){ Unwrap }
7
7
  it_should_behave_like("An operator class")
8
8
 
9
9
  let(:input) {[
10
- {:nested => {:a => "a", :b => "b"}, :c => "c"}
10
+ {:wrapped => {:a => "a", :b => "b"}, :c => "c"}
11
11
  ]}
12
12
 
13
13
  let(:expected) {[
@@ -17,13 +17,13 @@ module Alf
17
17
  subject{ operator.to_a }
18
18
 
19
19
  describe "when factored with commandline args" do
20
- let(:operator){ Unnest.run(%w{-- nested}) }
20
+ let(:operator){ Unwrap.run(%w{-- wrapped}) }
21
21
  before{ operator.pipe(input) }
22
22
  it { should == expected }
23
23
  end
24
24
 
25
25
  describe "when factored with Lispy" do
26
- let(:operator){ Lispy.unnest(input, :nested) }
26
+ let(:operator){ Lispy.unwrap(input, :wrapped) }
27
27
  it { should == expected }
28
28
  end
29
29
 
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
  module Alf
3
3
  module Operator::Relational
4
- describe Nest do
4
+ describe Wrap do
5
5
 
6
- let(:operator_class){ Nest }
6
+ let(:operator_class){ Wrap }
7
7
  it_should_behave_like("An operator class")
8
8
 
9
9
  let(:input) {[
@@ -11,19 +11,19 @@ module Alf
11
11
  ]}
12
12
 
13
13
  let(:expected) {[
14
- {:nested => {:a => "a", :b => "b"}, :c => "c"}
14
+ {:wraped => {:a => "a", :b => "b"}, :c => "c"}
15
15
  ]}
16
16
 
17
17
  subject{ operator.to_a }
18
18
 
19
19
  describe "when factored with commandline args" do
20
- let(:operator){ Nest.run(["--", "a", "b", "nested"]) }
20
+ let(:operator){ Wrap.run(["--", "a", "b", "wraped"]) }
21
21
  before{ operator.pipe(input) }
22
22
  it { should == expected }
23
23
  end
24
24
 
25
25
  describe "when factored with Lispy" do
26
- let(:operator){ Lispy.nest(input, [:a, :b], :nested) }
26
+ let(:operator){ Lispy.wrap(input, [:a, :b], :wraped) }
27
27
  it { should == expected }
28
28
  end
29
29
 
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Operator::NonRelational do
4
+
5
+ specify "each" do
6
+ x = []
7
+ Operator::NonRelational.each{|m| x << m}
8
+ x.sort{|m1,m2| m1.name.to_s <=> m2.name.to_s}.should == [
9
+ Alf::Operator::NonRelational::Autonum,
10
+ Alf::Operator::NonRelational::Clip,
11
+ Alf::Operator::NonRelational::Compact,
12
+ Alf::Operator::NonRelational::Defaults,
13
+ Alf::Operator::NonRelational::Sort,
14
+ ]
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Operator::Relational do
4
+
5
+ specify "each" do
6
+ x = []
7
+ Operator::Relational.each{|m| x << m}
8
+ x.sort{|m1,m2| m1.name.to_s <=> m2.name.to_s}.should == [
9
+ Alf::Operator::Relational::Extend,
10
+ Alf::Operator::Relational::Group,
11
+ Alf::Operator::Relational::Intersect,
12
+ Alf::Operator::Relational::Join,
13
+ Alf::Operator::Relational::Minus,
14
+ Alf::Operator::Relational::Project,
15
+ Alf::Operator::Relational::Quota,
16
+ Alf::Operator::Relational::Rename,
17
+ Alf::Operator::Relational::Restrict,
18
+ Alf::Operator::Relational::Summarize,
19
+ Alf::Operator::Relational::Ungroup,
20
+ Alf::Operator::Relational::Union,
21
+ Alf::Operator::Relational::Unwrap,
22
+ Alf::Operator::Relational::Wrap,
23
+ ]
24
+ end
25
+
26
+ end
27
+ end
File without changes
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Reader::AlfFile do
4
+
5
+ let(:io){ StringIO.new(expr) }
6
+ def dataset(name)
7
+ [{:status => 10},{:status => 30}]
8
+ end
9
+ subject{ Reader::AlfFile.new(io, self).to_a }
10
+
11
+ describe "on pure functional expressions" do
12
+ let(:expr){ "(restrict :suppliers, lambda{status > 20})" }
13
+ it{ should == [{:status => 30}]}
14
+ end
15
+
16
+ describe "on impure functional expressions" do
17
+ let(:expr){
18
+ <<-EOF
19
+ xxx = (restrict :suppliers, lambda{status > 20})
20
+ (extend xxx, :rev => lambda{ -status })
21
+ EOF
22
+ }
23
+ it{ should == [{:status => 30, :rev => -30}]}
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Relation do
4
+
5
+ let(:tuples_by_sid){[
6
+ {:sid => 'S1', :name => 'Smith', :status => 20, :city => 'London'},
7
+ {:sid => 'S2', :name => 'Jones', :status => 10, :city => 'Paris'},
8
+ {:sid => 'S3', :name => 'Blake', :status => 30, :city => 'Paris'}
9
+ ]}
10
+ let(:rel){ Relation.new tuples_by_sid.to_set }
11
+ let(:tuples_by_name){ tuples_by_sid.sort{|t1,t2| t1[:name] <=> t2[:name]} }
12
+
13
+ describe "coerce" do
14
+
15
+ subject{ Relation.coerce(arg) }
16
+
17
+ describe "with a Relation" do
18
+ let(:arg){ rel }
19
+ it{ should be_a(Relation) }
20
+ it{ should == rel }
21
+ end
22
+
23
+ describe "with a set of tuples" do
24
+ let(:arg){ tuples_by_name.to_set }
25
+ it{ should be_a(Relation) }
26
+ it{ should == rel }
27
+ end
28
+
29
+ describe "with an array of tuples" do
30
+ let(:arg){ tuples_by_name }
31
+ it{ should be_a(Relation) }
32
+ it{ should == rel }
33
+ end
34
+
35
+ describe "with an iterator" do
36
+ let(:arg){ Lispy.restrict(tuples_by_name, lambda{ true }) }
37
+ it{ should be_a(Relation) }
38
+ it{ should == rel }
39
+ end
40
+
41
+ describe "with unrecognized arg" do
42
+ let(:arg){ nil }
43
+ specify{ lambda{ subject }.should raise_error(ArgumentError) }
44
+ end
45
+
46
+ it "should be semi-aliased as []" do
47
+ Alf::Relation[*tuples_by_name].should == rel
48
+ end
49
+
50
+ end # coerce
51
+
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Relation do
4
+
5
+ let(:rel){Relation.coerce([
6
+ {:sid => 'S1'},
7
+ {:sid => 'S2'},
8
+ {:sid => 'S3'}
9
+ ])}
10
+
11
+ describe "inspect" do
12
+
13
+ it "should allows the litteral round-trip" do
14
+ Kernel.eval(rel.inspect).should == rel
15
+ end
16
+
17
+ end # coerce
18
+
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Relation do
4
+
5
+ let(:rel1){rel(
6
+ {:sid => 'S1'},
7
+ {:sid => 'S2'},
8
+ {:sid => 'S3'}
9
+ )}
10
+
11
+ let(:rel2){rel(
12
+ {:sid => 'S5'},
13
+ {:sid => 'S2'}
14
+ )}
15
+
16
+ specify "extend" do
17
+ rel1.extend(:x => lambda{ sid.downcase }).should == rel(
18
+ {:sid => 'S1', :x => 's1'},
19
+ {:sid => 'S2', :x => 's2'},
20
+ {:sid => 'S3', :x => 's3'}
21
+ )
22
+ end
23
+
24
+ specify "union" do
25
+ (rel1 + rel1).should == rel1
26
+ (rel1 + rel2).should == rel(
27
+ {:sid => 'S1'},
28
+ {:sid => 'S3'},
29
+ {:sid => 'S2'},
30
+ {:sid => 'S5'}
31
+ )
32
+ end # coerce
33
+
34
+ specify "difference" do
35
+ (rel1 - rel1).should == rel()
36
+ (rel1 - rel2).should == rel(
37
+ {:sid => 'S1'},
38
+ {:sid => 'S3'}
39
+ )
40
+ (rel2 - rel1).should == rel(
41
+ {:sid => 'S5'}
42
+ )
43
+ end # coerce
44
+
45
+ end
46
+ end
@@ -42,16 +42,16 @@ module Alf
42
42
  end
43
43
 
44
44
  it "should behave correctly on group" do
45
- Aggregator.group(:a).aggregate(input).should == [
45
+ Aggregator.group(:a).aggregate(input).should == rel(
46
46
  {:a => 1},
47
47
  {:a => 2},
48
- {:a => 3},
49
- ]
50
- Aggregator.group(:a, :sign).aggregate(input).should == [
48
+ {:a => 3}
49
+ )
50
+ Aggregator.group(:a, :sign).aggregate(input).should == rel(
51
51
  {:a => 1, :sign => -1},
52
52
  {:a => 2, :sign => 1 },
53
- {:a => 3, :sign => -1},
54
- ]
53
+ {:a => 3, :sign => -1}
54
+ )
55
55
  end
56
56
 
57
57
  it "should allow specific tuple computations" do