alf 0.9.3 → 0.10.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/CHANGELOG.md +255 -129
- data/Gemfile +31 -1
- data/Gemfile.lock +17 -20
- data/LICENCE.md +1 -1
- data/Manifest.txt +2 -0
- data/README.md +37 -43
- data/TODO.md +1 -1
- data/alf.gemspec +10 -7
- data/alf.noespec +24 -13
- data/bin/alf +2 -2
- data/doc/commands/exec.md +16 -0
- data/doc/commands/help.md +11 -0
- data/doc/commands/main.md +33 -0
- data/doc/commands/show.md +19 -0
- data/doc/operators/non_relational/autonum.md +23 -0
- data/doc/operators/non_relational/clip.md +31 -0
- data/doc/operators/non_relational/coerce.md +15 -0
- data/doc/operators/non_relational/compact.md +20 -0
- data/doc/operators/non_relational/defaults.md +32 -0
- data/doc/operators/non_relational/generator.md +20 -0
- data/doc/operators/non_relational/sort.md +24 -0
- data/doc/operators/relational/extend.md +18 -0
- data/doc/operators/relational/group.md +27 -0
- data/doc/operators/relational/intersect.md +13 -0
- data/doc/operators/relational/join.md +27 -0
- data/doc/operators/relational/matching.md +20 -0
- data/doc/operators/relational/minus.md +12 -0
- data/doc/operators/relational/not-matching.md +20 -0
- data/doc/operators/relational/project.md +28 -0
- data/doc/operators/relational/quota.md +21 -0
- data/doc/operators/relational/rank.md +27 -0
- data/doc/operators/relational/rename.md +17 -0
- data/doc/operators/relational/restrict.md +25 -0
- data/doc/operators/relational/summarize.md +25 -0
- data/doc/operators/relational/ungroup.md +20 -0
- data/doc/operators/relational/union.md +14 -0
- data/doc/operators/relational/unwrap.md +20 -0
- data/doc/operators/relational/wrap.md +24 -0
- data/examples/csv/suppliers.csv +6 -0
- data/examples/logs/access.log +1000 -0
- data/examples/logs/combined.alf +2 -0
- data/examples/logs/hits.alf +14 -0
- data/examples/logs/not_found.alf +7 -0
- data/examples/logs/robots-cheating.alf +11 -0
- data/examples/logs/robots.alf +8 -0
- data/examples/northwind/customers.csv +92 -0
- data/examples/northwind/northwind.db +0 -0
- data/examples/northwind/orders.csv +831 -0
- data/examples/operators/clip.alf +1 -1
- data/examples/operators/database.alf +5 -6
- data/examples/operators/defaults.alf +1 -1
- data/examples/operators/group.alf +1 -1
- data/examples/operators/project.alf +2 -1
- data/examples/operators/pseudo-with.alf +2 -2
- data/examples/operators/quota.alf +2 -2
- data/examples/operators/summarize.alf +2 -2
- data/lib/alf/aggregator/aggregators.rb +77 -0
- data/lib/alf/aggregator/base.rb +95 -0
- data/lib/alf/aggregator/class_methods.rb +57 -0
- data/lib/alf/buffer/sorted.rb +48 -0
- data/lib/alf/command/class_methods.rb +27 -0
- data/lib/alf/command/doc_manager.rb +72 -0
- data/lib/alf/command/exec.rb +12 -0
- data/lib/alf/command/help.rb +31 -0
- data/lib/alf/command/main.rb +146 -0
- data/lib/alf/command/show.rb +33 -0
- data/lib/alf/environment/base.rb +37 -0
- data/lib/alf/environment/class_methods.rb +93 -0
- data/lib/alf/environment/explicit.rb +38 -0
- data/lib/alf/environment/folder.rb +62 -0
- data/lib/alf/extra/csv.rb +104 -0
- data/lib/alf/extra/logs.rb +100 -0
- data/lib/alf/extra/sequel.rb +77 -0
- data/lib/alf/{yaml.rb → extra/yaml.rb} +0 -0
- data/lib/alf/extra.rb +5 -0
- data/lib/alf/iterator/base.rb +38 -0
- data/lib/alf/iterator/class_methods.rb +22 -0
- data/lib/alf/iterator/proxy.rb +33 -0
- data/lib/alf/lispy/instance_methods.rb +157 -0
- data/lib/alf/operator/base.rb +74 -0
- data/lib/alf/operator/binary.rb +32 -0
- data/lib/alf/operator/cesure.rb +45 -0
- data/lib/alf/operator/class_methods.rb +132 -0
- data/lib/alf/operator/experimental.rb +9 -0
- data/lib/alf/operator/non_relational/autonum.rb +24 -0
- data/lib/alf/operator/non_relational/clip.rb +20 -0
- data/lib/alf/operator/non_relational/coerce.rb +21 -0
- data/lib/alf/operator/non_relational/compact.rb +62 -0
- data/lib/alf/operator/non_relational/defaults.rb +25 -0
- data/lib/alf/operator/non_relational/generator.rb +38 -0
- data/lib/alf/operator/non_relational/sort.rb +23 -0
- data/lib/alf/operator/nullary.rb +20 -0
- data/lib/alf/operator/relational/extend.rb +24 -0
- data/lib/alf/operator/relational/group.rb +32 -0
- data/lib/alf/operator/relational/intersect.rb +37 -0
- data/lib/alf/operator/relational/join.rb +106 -0
- data/lib/alf/operator/relational/matching.rb +45 -0
- data/lib/alf/operator/relational/minus.rb +37 -0
- data/lib/alf/operator/relational/not_matching.rb +45 -0
- data/lib/alf/operator/relational/project.rb +22 -0
- data/lib/alf/operator/relational/quota.rb +51 -0
- data/lib/alf/operator/relational/rank.rb +55 -0
- data/lib/alf/operator/relational/rename.rb +19 -0
- data/lib/alf/operator/relational/restrict.rb +20 -0
- data/lib/alf/operator/relational/summarize.rb +83 -0
- data/lib/alf/operator/relational/ungroup.rb +25 -0
- data/lib/alf/operator/relational/union.rb +32 -0
- data/lib/alf/operator/relational/unwrap.rb +21 -0
- data/lib/alf/operator/relational/wrap.rb +22 -0
- data/lib/alf/operator/shortcut.rb +53 -0
- data/lib/alf/operator/signature.rb +262 -0
- data/lib/alf/operator/transform.rb +27 -0
- data/lib/alf/operator/unary.rb +38 -0
- data/lib/alf/reader/alf_file.rb +24 -0
- data/lib/alf/reader/base.rb +119 -0
- data/lib/alf/reader/class_methods.rb +82 -0
- data/lib/alf/reader/rash.rb +28 -0
- data/lib/alf/relation/class_methods.rb +37 -0
- data/lib/alf/relation/instance_methods.rb +127 -0
- data/lib/alf/renderer/base.rb +72 -0
- data/lib/alf/renderer/class_methods.rb +58 -0
- data/lib/alf/renderer/rash.rb +19 -0
- data/lib/alf/{text.rb → renderer/text.rb} +1 -1
- data/lib/alf/tools/coerce.rb +14 -0
- data/lib/alf/tools/miscellaneous.rb +77 -0
- data/lib/alf/tools/to_lispy.rb +99 -0
- data/lib/alf/tools/to_ruby_literal.rb +14 -0
- data/lib/alf/tools/tuple_handle.rb +50 -0
- data/lib/alf/types/attr_list.rb +56 -0
- data/lib/alf/types/attr_name.rb +28 -0
- data/lib/alf/types/boolean.rb +12 -0
- data/lib/alf/types/heading.rb +96 -0
- data/lib/alf/types/ordering.rb +93 -0
- data/lib/alf/types/renaming.rb +57 -0
- data/lib/alf/types/summarization.rb +76 -0
- data/lib/alf/types/tuple_computation.rb +61 -0
- data/lib/alf/types/tuple_expression.rb +61 -0
- data/lib/alf/types/tuple_predicate.rb +49 -0
- data/lib/alf/version.rb +2 -2
- data/lib/alf.rb +193 -3714
- data/spec/integration/__database__/group.alf +1 -1
- data/spec/integration/__database__/suppliers_csv.csv +6 -0
- data/spec/integration/command/alf/alf.db +0 -0
- data/spec/integration/command/alf/alf_env_sqlite.cmd +1 -0
- data/spec/integration/command/alf/alf_env_sqlite.stdout +9 -0
- data/spec/integration/command/alf/alf_help.cmd +1 -0
- data/spec/integration/command/alf/alf_help.stdout +67 -0
- data/spec/integration/command/autonum/autonum_0.cmd +1 -1
- data/spec/integration/command/coerce/coerce_1.cmd +1 -0
- data/spec/integration/command/coerce/coerce_1.stdout +5 -0
- data/spec/integration/command/defaults/defaults_0.cmd +1 -1
- data/spec/integration/command/defaults/defaults_0.stdout +9 -9
- data/spec/integration/command/defaults/defaults_2.cmd +1 -0
- data/spec/integration/command/defaults/defaults_2.stdout +9 -0
- data/spec/integration/command/generator/generator_1.cmd +1 -0
- data/spec/integration/command/generator/generator_1.stdout +10 -0
- data/spec/integration/command/generator/generator_2.cmd +1 -0
- data/spec/integration/command/generator/generator_2.stdout +5 -0
- data/spec/integration/command/generator/generator_3.cmd +1 -0
- data/spec/integration/command/generator/generator_3.stdout +5 -0
- data/spec/integration/command/group/group_0.cmd +1 -1
- data/spec/integration/command/group/group_1.cmd +1 -1
- data/spec/integration/command/help/help_1.cmd +1 -0
- data/spec/integration/command/help/help_1.stdout +22 -0
- data/spec/integration/command/quota/quota_0.cmd +1 -1
- data/spec/integration/command/rank/rank_1.cmd +1 -1
- data/spec/integration/command/rank/rank_1.stdout +10 -10
- data/spec/integration/command/rank/rank_2.cmd +1 -1
- data/spec/integration/command/rank/rank_2.stdout +10 -10
- data/spec/integration/command/rank/rank_3.cmd +1 -1
- data/spec/integration/command/rank/rank_3.stdout +10 -10
- data/spec/integration/command/rank/rank_4.cmd +1 -1
- data/spec/integration/command/rank/rank_5.cmd +1 -1
- data/spec/integration/command/show/show_csv.cmd +1 -0
- data/spec/integration/command/show/show_csv.stdout +6 -0
- data/spec/integration/command/show/show_rash_2.cmd +1 -1
- data/spec/integration/command/show/show_rash_2.stdout +5 -5
- data/spec/integration/command/sort/sort_0.cmd +1 -1
- data/spec/integration/command/sort/sort_1.cmd +1 -1
- data/spec/integration/command/sort/sort_1.stdout +2 -2
- data/spec/integration/command/sort/sort_2.cmd +1 -0
- data/spec/integration/command/sort/sort_2.stdout +9 -0
- data/spec/integration/command/sort/sort_3.cmd +1 -0
- data/spec/integration/command/sort/sort_3.stdout +9 -0
- data/spec/integration/command/summarize/summarize_0.cmd +1 -1
- data/spec/integration/command/ungroup/ungroup_0.cmd +1 -1
- data/spec/integration/command/wrap/wrap_0.cmd +1 -1
- data/spec/integration/semantics/test_project.alf +5 -6
- data/spec/integration/semantics/test_rank.alf +16 -16
- data/spec/integration/test_command.rb +17 -6
- data/spec/integration/test_examples.rb +1 -1
- data/spec/regression/logs/apache_combined.log +5 -0
- data/spec/regression/logs/test_path_attribute.rb +25 -0
- data/spec/regression/relation/test_relation_allbut_all.rb +14 -0
- data/spec/shared/an_operator_class.rb +10 -5
- data/spec/spec_helper.rb +1 -7
- data/spec/unit/assumptions/test_set.rb +64 -0
- data/spec/unit/command/doc_manager/dynamic.md +1 -0
- data/spec/unit/command/doc_manager/example.md +1 -0
- data/spec/unit/command/doc_manager/example_1.txt +11 -0
- data/spec/unit/command/doc_manager/static.md +1 -0
- data/spec/unit/command/doc_manager/test_call.rb +49 -0
- data/spec/unit/csv/input.csv +3 -0
- data/spec/unit/csv/test_reader.rb +66 -0
- data/spec/unit/csv/test_renderer.rb +73 -0
- data/spec/unit/lispy/test_relation.rb +37 -0
- data/spec/unit/lispy/test_run.rb +40 -0
- data/spec/unit/lispy/test_tuple.rb +36 -0
- data/spec/unit/logs/apache_combined.log +5 -0
- data/spec/unit/logs/postgresql.log +29 -0
- data/spec/unit/logs/test_reader.rb +56 -0
- data/spec/unit/operator/non_relational/compact/{buffer_based.rb → test_buffer_based.rb} +0 -0
- data/spec/unit/operator/non_relational/test_clip.rb +1 -1
- data/spec/unit/operator/non_relational/test_coerce.rb +35 -0
- data/spec/unit/operator/non_relational/test_defaults.rb +15 -2
- data/spec/unit/operator/non_relational/test_generator.rb +78 -0
- data/spec/unit/operator/relational/join/test_hash_based.rb +4 -4
- data/spec/unit/operator/relational/matching/test_hash_based.rb +6 -6
- data/spec/unit/operator/relational/not_matching/test_hash_based.rb +4 -4
- data/spec/unit/operator/relational/summarize/test_hash_based.rb +10 -6
- data/spec/unit/operator/relational/summarize/test_sort_based.rb +18 -7
- data/spec/unit/operator/relational/test_group.rb +8 -8
- data/spec/unit/operator/relational/test_intersect.rb +3 -3
- data/spec/unit/operator/relational/test_minus.rb +3 -3
- data/spec/unit/operator/relational/test_project.rb +12 -2
- data/spec/unit/operator/relational/test_quota.rb +5 -6
- data/spec/unit/operator/relational/test_summarize.rb +9 -11
- data/spec/unit/operator/relational/test_union.rb +1 -1
- data/spec/unit/operator/relational/test_wrap.rb +1 -1
- data/spec/unit/operator/signature/test_collect_on.rb +45 -0
- data/spec/unit/operator/signature/test_initialize.rb +17 -0
- data/spec/unit/operator/signature/test_install.rb +56 -0
- data/spec/unit/operator/signature/test_option_parser.rb +36 -0
- data/spec/unit/operator/signature/test_parse_args.rb +60 -0
- data/spec/unit/operator/signature/test_parse_argv.rb +87 -0
- data/spec/unit/operator/signature/test_to_lispy.rb +102 -0
- data/spec/unit/operator/signature/test_to_shell.rb +103 -0
- data/spec/unit/operator/test_non_relational.rb +3 -1
- data/spec/unit/relation/test_relops.rb +20 -15
- data/spec/unit/sequel/alf.db +0 -0
- data/spec/unit/sequel/test_environment.rb +54 -0
- data/spec/unit/test_aggregator.rb +32 -22
- data/spec/unit/test_environment.rb +5 -0
- data/spec/unit/test_lispy.rb +4 -0
- data/spec/unit/test_relation.rb +5 -0
- data/spec/unit/text/test_cell.rb +6 -6
- data/spec/unit/text/test_row.rb +3 -3
- data/spec/unit/text/test_table.rb +6 -6
- data/spec/unit/tools/test_coalesce.rb +15 -0
- data/spec/unit/tools/test_coerce.rb +10 -0
- data/spec/unit/tools/test_to_lispy.rb +138 -0
- data/spec/unit/tools/test_to_ruby_literal.rb +10 -0
- data/spec/unit/tools/test_tuple_handle.rb +1 -59
- data/spec/unit/types/test_attr_list.rb +106 -0
- data/spec/unit/types/test_attr_name.rb +52 -0
- data/spec/unit/{test_heading.rb → types/test_heading.rb} +10 -0
- data/spec/unit/types/test_ordering.rb +127 -0
- data/spec/unit/types/test_renaming.rb +55 -0
- data/spec/unit/types/test_summarization.rb +63 -0
- data/spec/unit/types/test_tuple_computation.rb +60 -0
- data/spec/unit/types/test_tuple_expression.rb +64 -0
- data/spec/unit/types/test_tuple_predicate.rb +79 -0
- data/tasks/debug_mail.rake +1 -1
- data/tasks/debug_mail.txt +5 -0
- data/tasks/gh-pages.rake +63 -0
- metadata +325 -52
- data/spec/unit/operator/test_command_methods.rb +0 -38
- data/spec/unit/tools/test_ordering_key.rb +0 -94
- data/spec/unit/tools/test_parse_commandline_args.rb +0 -47
- data/spec/unit/tools/test_projection_key.rb +0 -83
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Lispy
|
|
3
|
+
|
|
4
|
+
alias :ruby_extend :extend
|
|
5
|
+
|
|
6
|
+
# The environment
|
|
7
|
+
attr_accessor :environment
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Compiles a query expression given by a String or a block and returns
|
|
11
|
+
# the result (typically a tuple iterator)
|
|
12
|
+
#
|
|
13
|
+
# Example
|
|
14
|
+
#
|
|
15
|
+
# # with a string
|
|
16
|
+
# op = compile "(restrict :suppliers, lambda{ city == 'London' })"
|
|
17
|
+
#
|
|
18
|
+
# # or with a block
|
|
19
|
+
# op = compile {
|
|
20
|
+
# (restrict :suppliers, lambda{ city == 'London' })
|
|
21
|
+
# }
|
|
22
|
+
#
|
|
23
|
+
# @param [String] expr a Lispy expression to compile
|
|
24
|
+
# @return [Iterator] the iterator resulting from compilation
|
|
25
|
+
#
|
|
26
|
+
def compile(expr = nil, path = nil, &block)
|
|
27
|
+
if expr.nil?
|
|
28
|
+
instance_eval(&block)
|
|
29
|
+
else
|
|
30
|
+
b = _clean_binding
|
|
31
|
+
(path ? Kernel.eval(expr, b, path) : Kernel.eval(expr, b))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Evaluates a query expression given by a String or a block and returns
|
|
37
|
+
# the result as an in-memory relation (Alf::Relation)
|
|
38
|
+
#
|
|
39
|
+
# Example:
|
|
40
|
+
#
|
|
41
|
+
# # with a string
|
|
42
|
+
# rel = evaluate "(restrict :suppliers, lambda{ city == 'London' })"
|
|
43
|
+
#
|
|
44
|
+
# # or with a block
|
|
45
|
+
# rel = evaluate {
|
|
46
|
+
# (restrict :suppliers, lambda{ city == 'London' })
|
|
47
|
+
# }
|
|
48
|
+
#
|
|
49
|
+
def evaluate(expr = nil, path = nil, &block)
|
|
50
|
+
compiled = compile(expr, path, &block)
|
|
51
|
+
case compiled
|
|
52
|
+
when Iterator
|
|
53
|
+
compiled.to_rel
|
|
54
|
+
else
|
|
55
|
+
compiled
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# Coerces `h` to a valid tuple.
|
|
61
|
+
#
|
|
62
|
+
# @param [Hash] h, a hash mapping symbols to values
|
|
63
|
+
#
|
|
64
|
+
def Tuple(h)
|
|
65
|
+
unless h.keys.all?{|k| k.is_a?(Symbol)} &&
|
|
66
|
+
h.values.all?{|v| !v.nil?}
|
|
67
|
+
raise ArgumentError, "Invalid tuple literal #{h.inspect}"
|
|
68
|
+
end
|
|
69
|
+
h
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# Coerces `args` to a valid relation.
|
|
74
|
+
#
|
|
75
|
+
def Relation(first, *args)
|
|
76
|
+
if args.empty?
|
|
77
|
+
if first.is_a?(Symbol)
|
|
78
|
+
environment.dataset(first).to_rel
|
|
79
|
+
elsif first.is_a?(Hash)
|
|
80
|
+
Alf::Relation[first]
|
|
81
|
+
else
|
|
82
|
+
raise ArgumentError, "Unable to coerce `#{first.inspect}` to a relation"
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
Alf::Relation[*args.unshift(first)]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
# Install the DSL through iteration over defined operators
|
|
91
|
+
#
|
|
92
|
+
Operator.each do |op_class|
|
|
93
|
+
meth_name = Tools.ruby_case(Tools.class_name(op_class)).to_sym
|
|
94
|
+
if op_class.unary?
|
|
95
|
+
define_method(meth_name) do |child, *args|
|
|
96
|
+
child = Iterator.coerce(child, environment)
|
|
97
|
+
op_class.new(*args).pipe(child, environment)
|
|
98
|
+
end
|
|
99
|
+
elsif op_class.binary?
|
|
100
|
+
define_method(meth_name) do |left, right, *args|
|
|
101
|
+
operands = [left, right].collect{|x| Iterator.coerce(x, environment)}
|
|
102
|
+
op_class.new(*args).pipe(operands, environment)
|
|
103
|
+
end
|
|
104
|
+
elsif op_class.nullary?
|
|
105
|
+
define_method(meth_name) do |*args|
|
|
106
|
+
op_class.new(*args).pipe(nil, environment)
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
raise "Unexpected operator #{op_class}"
|
|
110
|
+
end
|
|
111
|
+
end # Operators::each
|
|
112
|
+
|
|
113
|
+
#
|
|
114
|
+
# Install the DSL through iteration over defined aggregators
|
|
115
|
+
#
|
|
116
|
+
Aggregator.each do |agg_class|
|
|
117
|
+
agg_name = Tools.ruby_case(Tools.class_name(agg_class)).to_sym
|
|
118
|
+
if method_defined?(agg_name)
|
|
119
|
+
raise "Unexpected method clash on Lispy: #{agg_name}"
|
|
120
|
+
else
|
|
121
|
+
define_method(agg_name) do |*args, &block|
|
|
122
|
+
agg_class.new(*args, &block)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def allbut(child, attributes)
|
|
128
|
+
(project child, attributes, :allbut => true)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Runs a command as in shell.
|
|
133
|
+
#
|
|
134
|
+
# Example:
|
|
135
|
+
#
|
|
136
|
+
# lispy = Alf.lispy(Alf::Environment.examples)
|
|
137
|
+
# op = lispy.run(['restrict', 'suppliers', '--', "city == 'Paris'"])
|
|
138
|
+
#
|
|
139
|
+
def run(argv, requester = nil)
|
|
140
|
+
argv = Quickl.parse_commandline_args(argv) if argv.is_a?(String)
|
|
141
|
+
argv = Quickl.split_commandline_args(argv, '|')
|
|
142
|
+
argv.inject(nil) do |cmd,arr|
|
|
143
|
+
arr.shift if arr.first == "alf"
|
|
144
|
+
main = Alf::Command::Main.new(environment)
|
|
145
|
+
main.stdin_reader = cmd unless cmd.nil?
|
|
146
|
+
main.run(arr, requester)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
def _clean_binding
|
|
153
|
+
binding
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end # module Lispy
|
|
157
|
+
end # module Alf
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator
|
|
3
|
+
#
|
|
4
|
+
# Contains all methods for operator instances
|
|
5
|
+
#
|
|
6
|
+
module Base
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Input datasets
|
|
10
|
+
#
|
|
11
|
+
attr_accessor :datasets
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# Optional environment
|
|
15
|
+
#
|
|
16
|
+
attr_accessor :environment
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# Create an operator instance
|
|
20
|
+
#
|
|
21
|
+
def initialize(*args)
|
|
22
|
+
signature.parse_args(args, self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Sets the operator input
|
|
27
|
+
#
|
|
28
|
+
def pipe(input, env = environment)
|
|
29
|
+
raise NotImplementedError, "Operator#pipe should be overriden"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Returns operator signature.
|
|
34
|
+
#
|
|
35
|
+
def signature
|
|
36
|
+
self.class.signature
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Yields each tuple in turn
|
|
41
|
+
#
|
|
42
|
+
# This method is implemented in a way that ensures that all operators are
|
|
43
|
+
# thread safe. It is not intended to be overriden, use _each instead.
|
|
44
|
+
#
|
|
45
|
+
def each
|
|
46
|
+
op = self.dup
|
|
47
|
+
op._prepare
|
|
48
|
+
op._each(&Proc.new)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
#
|
|
54
|
+
# Prepares the iterator before subsequent call to _each.
|
|
55
|
+
#
|
|
56
|
+
# This method is intended to be overriden by suclasses to install what's
|
|
57
|
+
# need for successful iteration. The default implementation does nothing.
|
|
58
|
+
#
|
|
59
|
+
def _prepare
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Internal implementation of the iterator.
|
|
63
|
+
#
|
|
64
|
+
# This method must be implemented by subclasses. It is safe to use instance
|
|
65
|
+
# variables (typically initialized in _prepare) here.
|
|
66
|
+
#
|
|
67
|
+
def _each
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private :datasets=, :environment=
|
|
71
|
+
end # module Base
|
|
72
|
+
include Base
|
|
73
|
+
end # module Operator
|
|
74
|
+
end # module Alf
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator
|
|
3
|
+
#
|
|
4
|
+
# Specialization of Operator for operators that work on a binary input
|
|
5
|
+
#
|
|
6
|
+
module Binary
|
|
7
|
+
include Operator
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Sets the operator input
|
|
11
|
+
#
|
|
12
|
+
def pipe(input, env = environment)
|
|
13
|
+
self.environment = env
|
|
14
|
+
self.datasets = input
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
# Returns the left operand
|
|
21
|
+
def left
|
|
22
|
+
Iterator.coerce(datasets.first, environment)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the right operand
|
|
26
|
+
def right
|
|
27
|
+
Iterator.coerce(datasets.last, environment)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end # module Binary
|
|
31
|
+
end # module Operator
|
|
32
|
+
end # module Alf
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator
|
|
3
|
+
#
|
|
4
|
+
# Specialization of Operator for implementing operators that rely on a
|
|
5
|
+
# cesure algorithm.
|
|
6
|
+
#
|
|
7
|
+
module Cesure
|
|
8
|
+
include Unary
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
# (see Operator#_each)
|
|
13
|
+
def _each
|
|
14
|
+
receiver, prev_key = Proc.new, nil
|
|
15
|
+
each_input_tuple do |tuple|
|
|
16
|
+
cur_key = project(tuple)
|
|
17
|
+
if cur_key != prev_key
|
|
18
|
+
flush_cesure(prev_key, receiver) unless prev_key.nil?
|
|
19
|
+
start_cesure(cur_key, receiver)
|
|
20
|
+
prev_key = cur_key
|
|
21
|
+
end
|
|
22
|
+
accumulate_cesure(tuple, receiver)
|
|
23
|
+
end
|
|
24
|
+
flush_cesure(prev_key, receiver) unless prev_key.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Projects a given tuple and returns it's cesure projection
|
|
28
|
+
def project(tuple)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Callback fired every time a new block starts
|
|
32
|
+
def start_cesure(key, receiver)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Callback fired on each tuple of the current block
|
|
36
|
+
def accumulate_cesure(tuple, receiver)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Callback fired at end of a block
|
|
40
|
+
def flush_cesure(key, receiver)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end # module Cesure
|
|
44
|
+
end # module Operator
|
|
45
|
+
end # module Alf
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator
|
|
3
|
+
#
|
|
4
|
+
# Encapsulates method that allows making operator introspection, that is,
|
|
5
|
+
# knowing operator cardinality and similar stuff.
|
|
6
|
+
#
|
|
7
|
+
module ClassMethods
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Returns false
|
|
11
|
+
#
|
|
12
|
+
def command?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Returns true
|
|
18
|
+
#
|
|
19
|
+
def operator?
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
#
|
|
24
|
+
# Returns true if this is a relational operator, false otherwise
|
|
25
|
+
#
|
|
26
|
+
def relational?
|
|
27
|
+
ancestors.include?(Relational)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
# Returns true if this is a non relational operator, false otherwise
|
|
32
|
+
#
|
|
33
|
+
def non_relational?
|
|
34
|
+
ancestors.include?(NonRelational)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Runs the command on commandline arguments
|
|
39
|
+
#
|
|
40
|
+
# @param [Array] argv an array of commandline arguments, typically ARGV
|
|
41
|
+
# @param [Object] req an optional requester, typically a super command
|
|
42
|
+
# @return [Iterator] an Iterator with query result
|
|
43
|
+
#
|
|
44
|
+
def run(argv, req = nil)
|
|
45
|
+
inst, operands = from_argv(argv)
|
|
46
|
+
|
|
47
|
+
# find standard input reader
|
|
48
|
+
stdin_reader = if req && req.respond_to?(:stdin_reader)
|
|
49
|
+
req.stdin_reader
|
|
50
|
+
else
|
|
51
|
+
Reader.coerce($stdin)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# normalize operands
|
|
55
|
+
operands = [ stdin_reader ] + Array(operands)
|
|
56
|
+
operands = operands.collect{|op|
|
|
57
|
+
Iterator.coerce(op, req && req.environment)
|
|
58
|
+
}
|
|
59
|
+
operands = if nullary?
|
|
60
|
+
[]
|
|
61
|
+
elsif unary?
|
|
62
|
+
operands.last
|
|
63
|
+
elsif binary?
|
|
64
|
+
operands[-2..-1]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
inst.pipe(operands, req && req.environment)
|
|
68
|
+
inst
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#
|
|
72
|
+
# Returns true if this operator is a zero-ary operator, false otherwise
|
|
73
|
+
#
|
|
74
|
+
def nullary?
|
|
75
|
+
ancestors.include?(Operator::Nullary)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Returns true if this operator is an unary operator, false otherwise
|
|
80
|
+
#
|
|
81
|
+
def unary?
|
|
82
|
+
ancestors.include?(Operator::Unary)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
#
|
|
86
|
+
# Returns true if this operator is a binary operator, false otherwise
|
|
87
|
+
#
|
|
88
|
+
def binary?
|
|
89
|
+
ancestors.include?(Operator::Binary)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# Installs or set the operator signature
|
|
94
|
+
#
|
|
95
|
+
def signature
|
|
96
|
+
if block_given?
|
|
97
|
+
@signature = Signature.new(self, &Proc.new)
|
|
98
|
+
@signature.install
|
|
99
|
+
options do |opt|
|
|
100
|
+
signature.fill_option_parser(opt, self)
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
@signature ||= Signature.new(self)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# Factors an operator instance from commandline arguments
|
|
110
|
+
def from_argv(argv)
|
|
111
|
+
inst = new
|
|
112
|
+
operands = inst.signature.parse_argv(argv, inst)
|
|
113
|
+
[inst, operands]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end # module ClassMethods
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# Yields non-relational then relational operators, in turn.
|
|
120
|
+
#
|
|
121
|
+
def self.each
|
|
122
|
+
Operator::NonRelational.each{|x| yield(x)}
|
|
123
|
+
Operator::Relational.each{|x| yield(x)}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Ensures that the Introspection module is set on real operators
|
|
127
|
+
def self.included(mod)
|
|
128
|
+
mod.extend(ClassMethods) if mod.is_a?(Class)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
end # module Operator
|
|
132
|
+
end # module Alf
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Autonum < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Transform
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
s.argument :as, AttrName, :autonum
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
# (see Operator#_prepare)
|
|
13
|
+
def _prepare
|
|
14
|
+
@autonum = -1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# (see Operator::Transform#_tuple2tuple)
|
|
18
|
+
def _tuple2tuple(tuple)
|
|
19
|
+
tuple.merge(@as => (@autonum += 1))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end # class Autonum
|
|
23
|
+
end # module Operator::NonRelational
|
|
24
|
+
end # module Alf
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Clip < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Transform
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
s.argument :attributes, AttrList, []
|
|
8
|
+
s.option :allbut, Boolean, false, "Apply an allbut clipping?"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
protected
|
|
12
|
+
|
|
13
|
+
# (see Operator::Transform#_tuple2tuple)
|
|
14
|
+
def _tuple2tuple(tuple)
|
|
15
|
+
@attributes.project(tuple, @allbut)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end # class Clip
|
|
19
|
+
end # module Operator::NonRelational
|
|
20
|
+
end # module Alf
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Coerce < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Transform
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
s.argument :heading, Heading, {}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
# (see Operator::Transform#_tuple2tuple)
|
|
13
|
+
def _tuple2tuple(tuple)
|
|
14
|
+
tuple.merge tuple_collect(@heading.attributes){|k,d|
|
|
15
|
+
[k, coerce(tuple[k], d)]
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end # class Coerce
|
|
20
|
+
end # module Operator::NonRelational
|
|
21
|
+
end # module Alf
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Compact < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Shortcut, Operator::Unary
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Removes duplicates according to a complete order
|
|
10
|
+
class SortBased
|
|
11
|
+
include Operator, Operator::Cesure
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@cesure_key ||= AttrList.new([])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
# (see Operator::Cesure#project)
|
|
20
|
+
def project(tuple)
|
|
21
|
+
@cesure_key.project(tuple, true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# (see Operator::Cesure#accumulate_cesure)
|
|
25
|
+
def accumulate_cesure(tuple, receiver)
|
|
26
|
+
@tuple = tuple
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# (see Operator::Cesure#flush_cesure)
|
|
30
|
+
def flush_cesure(key, receiver)
|
|
31
|
+
receiver.call(@tuple)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end # class SortBased
|
|
35
|
+
|
|
36
|
+
# Removes duplicates by loading all in memory and filtering
|
|
37
|
+
# them there
|
|
38
|
+
class BufferBased
|
|
39
|
+
include Operator, Operator::Unary
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
def _prepare
|
|
44
|
+
@tuples = input.to_a.uniq
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def _each
|
|
48
|
+
@tuples.each(&Proc.new)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end # class BufferBased
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def longexpr
|
|
56
|
+
chain BufferBased.new,
|
|
57
|
+
datasets
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end # class Compact
|
|
61
|
+
end # module Operator::NonRelational
|
|
62
|
+
end # module Alf
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Defaults < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Transform
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
s.argument :defaults, TupleComputation, {}
|
|
8
|
+
s.option :strict, Boolean, false, "Restrict to default attributes only?"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
protected
|
|
12
|
+
|
|
13
|
+
# (see Operator::Transform#_tuple2tuple)
|
|
14
|
+
def _tuple2tuple(tuple)
|
|
15
|
+
handle = TupleHandle.new.set(tuple)
|
|
16
|
+
defs = @defaults.evaluate(handle)
|
|
17
|
+
keys = @strict ? defs.keys : (tuple.keys | defs.keys)
|
|
18
|
+
tuple_collect(keys){|k|
|
|
19
|
+
[k, coalesce(tuple[k], defs[k])]
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # class Defaults
|
|
24
|
+
end # module Operator::NonRelational
|
|
25
|
+
end # module Alf
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Generator < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Nullary
|
|
5
|
+
|
|
6
|
+
class Size < Integer
|
|
7
|
+
extend Myrrha::Domain
|
|
8
|
+
|
|
9
|
+
def self.coerce(args)
|
|
10
|
+
Tools.coerce(args, Integer)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.from_argv(argv, opts = {})
|
|
14
|
+
coerce(argv.first)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.predicate
|
|
18
|
+
@predicate ||= lambda{|i| i >= 0}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end # Size
|
|
22
|
+
|
|
23
|
+
signature do |s|
|
|
24
|
+
s.argument :size, Size, 10
|
|
25
|
+
s.argument :as, AttrName, :num
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
def _each
|
|
31
|
+
size.times do |i|
|
|
32
|
+
yield(@as => i+1)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end # class Generator
|
|
37
|
+
end # module Operator::NonRelational
|
|
38
|
+
end # module Alf
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator::NonRelational
|
|
3
|
+
class Sort < Alf::Operator()
|
|
4
|
+
include Operator::NonRelational, Operator::Unary
|
|
5
|
+
|
|
6
|
+
signature do |s|
|
|
7
|
+
s.argument :ordering, Ordering, []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
def _prepare
|
|
13
|
+
@buffer = Buffer::Sorted.new(ordering)
|
|
14
|
+
@buffer.add_all(input)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def _each
|
|
18
|
+
@buffer.each(&Proc.new)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end # class Sort
|
|
22
|
+
end # module Operator::NonRelational
|
|
23
|
+
end # module Alf
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Alf
|
|
2
|
+
module Operator
|
|
3
|
+
#
|
|
4
|
+
# Specialization of Operator for operators without operands
|
|
5
|
+
#
|
|
6
|
+
module Nullary
|
|
7
|
+
include Operator
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# Sets the operator input
|
|
11
|
+
#
|
|
12
|
+
def pipe(input, env = environment)
|
|
13
|
+
self.environment = env
|
|
14
|
+
self.datasets = []
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end # module Nullary
|
|
19
|
+
end # module Operator
|
|
20
|
+
end # module Alf
|