alf 0.9.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.
Files changed (94) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +42 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +15 -0
  6. data/README.md +769 -0
  7. data/Rakefile +23 -0
  8. data/TODO.md +26 -0
  9. data/alf.gemspec +191 -0
  10. data/alf.noespec +30 -0
  11. data/bin/alf +31 -0
  12. data/examples/autonum.alf +6 -0
  13. data/examples/cities.rash +4 -0
  14. data/examples/clip.alf +3 -0
  15. data/examples/compact.alf +2 -0
  16. data/examples/database.alf +6 -0
  17. data/examples/defaults.alf +3 -0
  18. data/examples/extend.alf +3 -0
  19. data/examples/group.alf +3 -0
  20. data/examples/intersect.alf +4 -0
  21. data/examples/join.alf +2 -0
  22. data/examples/minus.alf +8 -0
  23. data/examples/nest.alf +2 -0
  24. data/examples/nulls.rash +3 -0
  25. data/examples/parts.rash +6 -0
  26. data/examples/project.alf +2 -0
  27. data/examples/quota.alf +4 -0
  28. data/examples/rename.alf +3 -0
  29. data/examples/restrict.alf +2 -0
  30. data/examples/runall.sh +26 -0
  31. data/examples/schema.yaml +28 -0
  32. data/examples/sort.alf +4 -0
  33. data/examples/summarize.alf +16 -0
  34. data/examples/suppliers.rash +5 -0
  35. data/examples/supplies.rash +12 -0
  36. data/examples/ungroup.alf +4 -0
  37. data/examples/union.alf +3 -0
  38. data/examples/unnest.alf +4 -0
  39. data/examples/with.alf +23 -0
  40. data/lib/alf.rb +2984 -0
  41. data/lib/alf/loader.rb +1 -0
  42. data/lib/alf/renderer/text.rb +153 -0
  43. data/lib/alf/renderer/yaml.rb +22 -0
  44. data/lib/alf/version.rb +14 -0
  45. data/spec/aggregator_spec.rb +62 -0
  46. data/spec/alf_spec.rb +47 -0
  47. data/spec/assumptions_spec.rb +15 -0
  48. data/spec/environment/explicit_spec.rb +15 -0
  49. data/spec/environment/folder_spec.rb +30 -0
  50. data/spec/examples_spec.rb +26 -0
  51. data/spec/lispy_spec.rb +23 -0
  52. data/spec/operator/command_methods_spec.rb +38 -0
  53. data/spec/operator/non_relational/autonum_spec.rb +61 -0
  54. data/spec/operator/non_relational/clip_spec.rb +49 -0
  55. data/spec/operator/non_relational/compact/buffer_based.rb +30 -0
  56. data/spec/operator/non_relational/compact/sort_based_spec.rb +30 -0
  57. data/spec/operator/non_relational/compact_spec.rb +38 -0
  58. data/spec/operator/non_relational/defaults_spec.rb +55 -0
  59. data/spec/operator/non_relational/sort_spec.rb +66 -0
  60. data/spec/operator/relational/extend_spec.rb +34 -0
  61. data/spec/operator/relational/group_spec.rb +54 -0
  62. data/spec/operator/relational/intersect_spec.rb +58 -0
  63. data/spec/operator/relational/join/hash_based_spec.rb +63 -0
  64. data/spec/operator/relational/minus_spec.rb +56 -0
  65. data/spec/operator/relational/nest_spec.rb +32 -0
  66. data/spec/operator/relational/project_spec.rb +65 -0
  67. data/spec/operator/relational/quota_spec.rb +44 -0
  68. data/spec/operator/relational/rename_spec.rb +32 -0
  69. data/spec/operator/relational/restrict_spec.rb +56 -0
  70. data/spec/operator/relational/summarize/sort_based_spec.rb +31 -0
  71. data/spec/operator/relational/summarize_spec.rb +41 -0
  72. data/spec/operator/relational/ungroup_spec.rb +35 -0
  73. data/spec/operator/relational/union_spec.rb +35 -0
  74. data/spec/operator/relational/unnest_spec.rb +32 -0
  75. data/spec/reader/alf_file_spec.rb +15 -0
  76. data/spec/reader/input.rb +2 -0
  77. data/spec/reader/rash_spec.rb +31 -0
  78. data/spec/reader_spec.rb +27 -0
  79. data/spec/renderer/text/cell_spec.rb +34 -0
  80. data/spec/renderer/text/row_spec.rb +30 -0
  81. data/spec/renderer/text/table_spec.rb +39 -0
  82. data/spec/renderer_spec.rb +42 -0
  83. data/spec/spec_helper.rb +26 -0
  84. data/spec/tools/ordering_key_spec.rb +81 -0
  85. data/spec/tools/projection_key_spec.rb +83 -0
  86. data/spec/tools/tools_spec.rb +25 -0
  87. data/spec/tools/tuple_handle_spec.rb +78 -0
  88. data/tasks/debug_mail.rake +78 -0
  89. data/tasks/debug_mail.txt +13 -0
  90. data/tasks/gem.rake +68 -0
  91. data/tasks/spec_test.rake +79 -0
  92. data/tasks/unit_test.rake +77 -0
  93. data/tasks/yard.rake +51 -0
  94. metadata +282 -0
@@ -0,0 +1 @@
1
+ require "quickl"
@@ -0,0 +1,153 @@
1
+ class Alf::Renderer
2
+ class Text < Alf::Renderer
3
+
4
+ module Utils
5
+
6
+ def looks_a_relation?(value)
7
+ value.is_a?(Alf::Iterator) or
8
+ (value.is_a?(Array) && !value.empty? && value.all?{|v| v.is_a?(Hash)})
9
+ end
10
+
11
+ def max(x, y)
12
+ return y if x.nil?
13
+ return x if y.nil?
14
+ x > y ? x : y
15
+ end
16
+
17
+ end
18
+ include Utils
19
+
20
+ class Cell
21
+ include Utils
22
+
23
+ def initialize(value)
24
+ @value = value
25
+ end
26
+
27
+ def min_width
28
+ @min_width ||= rendering_lines.inject(0) do |maxl,line|
29
+ max(maxl,line.size)
30
+ end
31
+ end
32
+
33
+ def rendering_lines(size = nil)
34
+ if size.nil?
35
+ text_rendering.split(/\n/)
36
+ elsif @value.is_a?(Numeric)
37
+ rendering_lines(nil).collect{|l| "%#{size}s" % l}
38
+ else
39
+ rendering_lines(nil).collect{|l| "%-#{size}s" % l}
40
+ end
41
+ end
42
+
43
+ def text_rendering
44
+ @text_rendering ||= case (value = @value)
45
+ when NilClass
46
+ "[nil]"
47
+ when Symbol
48
+ value.inspect
49
+ when Float
50
+ "%.7f" % value
51
+ when Hash
52
+ value.inspect
53
+ when Alf::Iterator
54
+ Text.render(value, "")
55
+ when Array
56
+ array_rendering(value)
57
+ else
58
+ value.to_s
59
+ end
60
+ end
61
+
62
+ def array_rendering(value)
63
+ if looks_a_relation?(value)
64
+ Text.render(value, "")
65
+ elsif value.empty?
66
+ "[]"
67
+ else
68
+ values = value.collect{|x| Cell.new(x).text_rendering}
69
+ if values.inject(0){|memo,s| memo + s.size} < 20
70
+ "[" + values.join(", ") + "]"
71
+ else
72
+ "[" + values.join(",\n ") + "]"
73
+ end
74
+ end
75
+ end
76
+
77
+ end # class Cell
78
+
79
+ class Row
80
+ include Utils
81
+
82
+ def initialize(values)
83
+ @cells = values.collect{|v| Cell.new(v)}
84
+ end
85
+
86
+ def min_widths
87
+ @cells.collect{|cell| cell.min_width}
88
+ end
89
+
90
+ def rendering_lines(sizes = min_widths)
91
+ nb_lines = 0
92
+ by_cell = @cells.zip(sizes).collect do |cell,size|
93
+ lines = cell.rendering_lines(size)
94
+ nb_lines = max(nb_lines, lines.size)
95
+ lines
96
+ end
97
+ grid = (0...nb_lines).collect do |line_i|
98
+ "| " + by_cell.zip(sizes).collect{|cell_lines, size|
99
+ cell_lines[line_i] || " "*size
100
+ }.join(" | ") + " |"
101
+ end
102
+ grid.empty? ? ["| |"] : grid
103
+ end
104
+
105
+ end # class Row
106
+
107
+ class Table
108
+ include Utils
109
+
110
+ def initialize(records, attributes)
111
+ @header = Row.new(attributes)
112
+ @rows = records.collect{|r| Row.new(r)}
113
+ end
114
+
115
+ def render(buffer = "")
116
+ sizes = @rows.inject(@header.min_widths) do |memo,row|
117
+ memo.zip(row.min_widths).collect{|x,y| max(x,y)}
118
+ end
119
+ sep = '+-' << sizes.collect{|s| '-' * s}.join('-+-') << '-+'
120
+ buffer << sep << "\n"
121
+ buffer << @header.rendering_lines(sizes).first << "\n"
122
+ buffer << sep << "\n"
123
+ @rows.each do |row|
124
+ row.rendering_lines(sizes).each do |line|
125
+ buffer << line << "\n"
126
+ end
127
+ end
128
+ buffer << sep << "\n"
129
+ buffer
130
+ end
131
+
132
+ end # class Table
133
+
134
+ protected
135
+
136
+ def render(input, output)
137
+ relation = input.to_a
138
+ attrs = relation.inject([]){|memo,t|
139
+ memo | t.keys
140
+ }
141
+ records = relation.collect{|t|
142
+ attrs.collect{|a| t[a]}
143
+ }
144
+ Table.new(records, attrs).render(output)
145
+ end
146
+
147
+ def self.render(input, output)
148
+ new(input).execute(output)
149
+ end
150
+
151
+ Alf::Renderer.register(:text, "as a text table", self)
152
+ end # class Text
153
+ end # class Alf
@@ -0,0 +1,22 @@
1
+ require "yaml"
2
+ module Alf
3
+ module Iterator
4
+
5
+ def to_yaml(*args, &block)
6
+ to_a.to_yaml(*args, &block)
7
+ end
8
+
9
+ end
10
+ class Renderer::YAML < Renderer
11
+
12
+ protected
13
+
14
+ # (see Alf::Renderer#render)
15
+ def render(input, output)
16
+ output << input.to_a.to_yaml << "\n"
17
+ output
18
+ end
19
+
20
+ Renderer.register(:yaml, "as a yaml output", self)
21
+ end # class YAML
22
+ end # module Alf
@@ -0,0 +1,14 @@
1
+ module Alf
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 9
6
+ TINY = 0
7
+
8
+ def self.to_s
9
+ [ MAJOR, MINOR, TINY ].join('.')
10
+ end
11
+
12
+ end
13
+ VERSION = Version.to_s
14
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Aggregator do
4
+
5
+ let(:input){[
6
+ {:a => 1, :sign => -1},
7
+ {:a => 2, :sign => 1 },
8
+ {:a => 3, :sign => -1},
9
+ {:a => 1, :sign => -1},
10
+ ]}
11
+
12
+ it "should behave correctly on count" do
13
+ Aggregator.count(:a).aggregate(input).should == 4
14
+ end
15
+
16
+ it "should behave correctly on sum" do
17
+ Aggregator.sum(:a).aggregate(input).should == 7
18
+ end
19
+
20
+ it "should behave correctly on avg" do
21
+ Aggregator.avg(:a).aggregate(input).should == 7.0 / 4.0
22
+ end
23
+
24
+ it "should behave correctly on min" do
25
+ Aggregator.min(:a).aggregate(input).should == 1
26
+ end
27
+
28
+ it "should behave correctly on max" do
29
+ Aggregator.max(:a).aggregate(input).should == 3
30
+ end
31
+
32
+ it "should behave correctly on concat" do
33
+ Aggregator.concat(:a).aggregate(input).should == "1231"
34
+ Aggregator.concat(:a, :between => " ").aggregate(input).should == "1 2 3 1"
35
+ Aggregator.concat(:a, :before => "[", :after => "]").aggregate(input).should == "[1231]"
36
+ Aggregator.concat(:before => "[", :after => "]"){ a }.aggregate(input).should == "[1231]"
37
+ end
38
+
39
+ it "should behave correctly on collect" do
40
+ Aggregator.collect(:a).aggregate(input).should == [1, 2, 3, 1]
41
+ Aggregator.collect{ {:a => a, :sign => sign} }.aggregate(input).should == input
42
+ end
43
+
44
+ it "should behave correctly on group" do
45
+ Aggregator.group(:a).aggregate(input).should == [
46
+ {:a => 1},
47
+ {:a => 2},
48
+ {:a => 3},
49
+ ]
50
+ Aggregator.group(:a, :sign).aggregate(input).should == [
51
+ {:a => 1, :sign => -1},
52
+ {:a => 2, :sign => 1 },
53
+ {:a => 3, :sign => -1},
54
+ ]
55
+ end
56
+
57
+ it "should allow specific tuple computations" do
58
+ Aggregator.sum{ 1.0 * a * sign }.aggregate(input).should == -3.0
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ describe Alf do
3
+
4
+ let(:lispy){ Alf.lispy(Alf::Environment.examples) }
5
+
6
+ let(:expected){[
7
+ {:status => 20, :sid=>"S1", :name=>"Smith", :city=>"London"},
8
+ {:status => 20, :sid=>"S4", :name=>"Clark", :city=>"London"}
9
+ ]}
10
+
11
+ it "should have a version number" do
12
+ Alf.const_defined?(:VERSION).should be_true
13
+ end
14
+
15
+ it "should allow running a commandline like command" do
16
+ op = lispy.run(['restrict', 'suppliers', '--', "city == 'London'"])
17
+ op.to_a.should == expected
18
+ end
19
+
20
+ it "should allow compiling lispy expressions" do
21
+ lispy.compile{
22
+ (restrict :suppliers, lambda{ city == 'London'})
23
+ }.to_a.should == expected
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
+
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
+ ]
45
+ end
46
+
47
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ describe :instance_eval do
3
+
4
+ let(:bar){ lambda{10} }
5
+ let(:foo) { Object.new }
6
+
7
+ if RUBY_VERSION <= "1.9"
8
+ subject{ foo.instance_eval(&bar) }
9
+ it { should == 10 }
10
+ else
11
+ subject{ foo.instance_exec(&bar) }
12
+ it { should == 10 }
13
+ end
14
+
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Environment::Explicit do
4
+
5
+ it "should allow branching easily" do
6
+ env = Environment::Explicit.new(:hello => "world")
7
+ env.dataset(:hello).should == "world"
8
+ env = env.branch(:hello => "world2")
9
+ env.dataset(:hello).should == "world2"
10
+ env = env.unbranch
11
+ env.dataset(:hello).should == "world"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Environment::Folder do
4
+
5
+ let(:path){ File.expand_path('../../../examples', __FILE__) }
6
+ let(:env){ Environment::Folder.new(path) }
7
+
8
+ describe "dataset" do
9
+
10
+ subject{ env.dataset(name) }
11
+
12
+ describe "when called on explicit file" do
13
+ let(:name){ "suppliers.rash" }
14
+ it{ should be_a(Reader::Rash) }
15
+ end
16
+
17
+ describe "when called on existing" do
18
+ let(:name){ "suppliers" }
19
+ it{ should be_a(Reader::Rash) }
20
+ end
21
+
22
+ describe "when called on unexisting" do
23
+ let(:name){ "notavalidone" }
24
+ specify{ lambda{ subject }.should raise_error }
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe "Alf's examples" do
4
+
5
+ shared_examples_for "An example" do
6
+
7
+ let(:env){ Environment.examples }
8
+
9
+ it "should work when executed with a Alf" do
10
+ lambda{
11
+ Alf.lispy(env).compile(File.read(subject)).to_a
12
+ }.should_not raise_error
13
+ end
14
+
15
+ end # An example
16
+
17
+
18
+ Dir["#{File.expand_path('../../examples', __FILE__)}/**/*.alf"].each do |file|
19
+ describe file do
20
+ subject{ file }
21
+ it_should_behave_like "An example"
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Lispy do
4
+ include Lispy
5
+
6
+ let(:input){[
7
+ {:tested => 1, :other => "b"},
8
+ {:tested => 30, :other => "a"},
9
+ ]}
10
+
11
+ let(:expected){[
12
+ {:tested => 30, :other => "a", :upcase => "A"},
13
+ ]}
14
+
15
+ it "should allow chaining operators 'ala' LISP" do
16
+ operator = (extend \
17
+ (restrict input, lambda{ tested > 10 }),
18
+ :upcase => lambda{ other.upcase })
19
+ operator.to_a.should == expected
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ module Alf
3
+ describe Operator::CommandMethods do
4
+
5
+ let(:cmd){ Object.new.extend(Operator::CommandMethods) }
6
+
7
+ describe "split_command_args" do
8
+
9
+ subject{ cmd.send(:split_command_args, args) }
10
+
11
+ describe "when applied to both operands and args" do
12
+ let(:args) { %w{op1 op2 -- a b c} }
13
+ let(:expected){ [ %w{op1 op2}, %w{a b c} ] }
14
+ it { should == expected }
15
+ end
16
+
17
+ describe "when applied to one operand only" do
18
+ let(:args) { %w{op1} }
19
+ let(:expected){ [ %w{op1}, %w{} ] }
20
+ it { should == expected }
21
+ end
22
+
23
+ describe "when applied to two operands only" do
24
+ let(:args) { %w{op1 op2} }
25
+ let(:expected){ [ %w{op1 op2}, %w{} ] }
26
+ it { should == expected }
27
+ end
28
+
29
+ describe "when applied to args only" do
30
+ let(:args) { %w{-- a b c} }
31
+ let(:expected){ [ [$stdin], %w{a b c} ] }
32
+ it { should == expected }
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end