alf 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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