alf 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,127 @@
|
|
1
|
+
module Alf
|
2
|
+
class Relation
|
3
|
+
module InstanceMethods
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
# @return [Set] the set of tuples
|
8
|
+
attr_reader :tuples
|
9
|
+
|
10
|
+
public
|
11
|
+
|
12
|
+
#
|
13
|
+
# Creates a Relation instance.
|
14
|
+
#
|
15
|
+
# @param [Set] tuples a set of tuples
|
16
|
+
#
|
17
|
+
def initialize(tuples)
|
18
|
+
raise ArgumentError unless tuples.is_a?(Set)
|
19
|
+
@tuples = tuples
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# (see Iterator#each)
|
24
|
+
#
|
25
|
+
def each(&block)
|
26
|
+
tuples.each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Returns relation's cardinality (number of tuples).
|
31
|
+
#
|
32
|
+
# @return [Integer] relation's cardinality
|
33
|
+
#
|
34
|
+
def cardinality
|
35
|
+
tuples.size
|
36
|
+
end
|
37
|
+
alias :size :cardinality
|
38
|
+
alias :count :cardinality
|
39
|
+
|
40
|
+
# Returns true if this relation is empty
|
41
|
+
def empty?
|
42
|
+
cardinality == 0
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Install the DSL through iteration over defined operators
|
47
|
+
#
|
48
|
+
Operator::each do |op_class|
|
49
|
+
meth_name = Tools.ruby_case(Tools.class_name(op_class)).to_sym
|
50
|
+
if op_class.unary?
|
51
|
+
define_method(meth_name) do |*args|
|
52
|
+
op = op_class.new(*args).pipe(self)
|
53
|
+
Relation.coerce(op)
|
54
|
+
end
|
55
|
+
elsif op_class.binary?
|
56
|
+
define_method(meth_name) do |right, *args|
|
57
|
+
op = op_class.new(*args).pipe([self, Iterator.coerce(right)])
|
58
|
+
Relation.coerce(op)
|
59
|
+
end
|
60
|
+
elsif op_class.nullary?
|
61
|
+
define_method(meth_name) do |*args|
|
62
|
+
op = op_class.new(*args)
|
63
|
+
Relation.coerce(op)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise "Unexpected operator #{op_class}"
|
67
|
+
end
|
68
|
+
end # Operators::each
|
69
|
+
|
70
|
+
alias :+ :union
|
71
|
+
alias :- :minus
|
72
|
+
|
73
|
+
# Shortcut for project(attributes, :allbut => true)
|
74
|
+
def allbut(attributes)
|
75
|
+
project(attributes, :allbut => true)
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# (see Object#hash)
|
80
|
+
#
|
81
|
+
def hash
|
82
|
+
@tuples.hash
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# (see Object#==)
|
87
|
+
#
|
88
|
+
def ==(other)
|
89
|
+
return nil unless other.is_a?(Relation)
|
90
|
+
other.tuples == self.tuples
|
91
|
+
end
|
92
|
+
alias :eql? :==
|
93
|
+
|
94
|
+
#
|
95
|
+
# Returns a textual representation of this relation
|
96
|
+
#
|
97
|
+
def to_s
|
98
|
+
Alf::Renderer.text(self).execute("")
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Returns an array with all tuples in this relation.
|
103
|
+
#
|
104
|
+
# @param [Tools::Ordering] an optional ordering key (any argument
|
105
|
+
# recognized by Ordering.coerce is supported here).
|
106
|
+
# @return [Array] an array of hashes, in requested order (if specified)
|
107
|
+
#
|
108
|
+
def to_a(okey = nil)
|
109
|
+
okey = Tools.coerce(okey, Ordering) if okey
|
110
|
+
ary = tuples.to_a
|
111
|
+
ary.sort!(&okey.sorter) if okey
|
112
|
+
ary
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Returns a literal representation of this relation
|
117
|
+
#
|
118
|
+
def to_ruby_literal
|
119
|
+
"Alf::Relation[" +
|
120
|
+
tuples.collect{|t| Tools.to_ruby_literal(t)}.join(', ') + "]"
|
121
|
+
end
|
122
|
+
alias :inspect :to_ruby_literal
|
123
|
+
|
124
|
+
end # module InstanceMethods
|
125
|
+
include(InstanceMethods)
|
126
|
+
end # class Relation
|
127
|
+
end # module Alf
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Alf
|
2
|
+
class Renderer
|
3
|
+
module Base
|
4
|
+
|
5
|
+
# Default renderer options
|
6
|
+
DEFAULT_OPTIONS = {}
|
7
|
+
|
8
|
+
# Renderer input (typically an Iterator)
|
9
|
+
attr_accessor :input
|
10
|
+
|
11
|
+
# @return [Environment] Optional wired environment
|
12
|
+
attr_accessor :environment
|
13
|
+
|
14
|
+
# @return [Hash] Renderer's options
|
15
|
+
attr_accessor :options
|
16
|
+
|
17
|
+
#
|
18
|
+
# Creates a reader instance.
|
19
|
+
#
|
20
|
+
# @param [Iterator] iterator an Iterator of tuples to render
|
21
|
+
# @param [Environment] environment wired environment, serving this reader
|
22
|
+
# @param [Hash] options Reader's options (see doc of subclasses)
|
23
|
+
#
|
24
|
+
def initialize(*args)
|
25
|
+
@input, @environment, @options = case args.first
|
26
|
+
when Array
|
27
|
+
Tools.varargs(args, [Array, Environment, Hash])
|
28
|
+
else
|
29
|
+
Tools.varargs(args, [Iterator, Environment, Hash])
|
30
|
+
end
|
31
|
+
@options = self.class.const_get(:DEFAULT_OPTIONS).merge(@options || {})
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Sets the renderer input.
|
36
|
+
#
|
37
|
+
# This method mimics {Iterator#pipe} and have the same contract.
|
38
|
+
#
|
39
|
+
def pipe(input, env = environment)
|
40
|
+
self.environment = env
|
41
|
+
self.input = input
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Executes the rendering, outputting the resulting tuples on the provided
|
47
|
+
# output buffer.
|
48
|
+
#
|
49
|
+
# The default implementation simply coerces the input as an Iterator and
|
50
|
+
# delegates the call to {#render}.
|
51
|
+
#
|
52
|
+
def execute(output = $stdout)
|
53
|
+
render(Iterator.coerce(input, environment), output)
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
#
|
59
|
+
# Renders tuples served by the iterator to the output buffer provided and
|
60
|
+
# returns the latter.
|
61
|
+
#
|
62
|
+
# This method must be implemented by subclasses unless {#execute} is
|
63
|
+
# overriden.
|
64
|
+
#
|
65
|
+
def render(iterator, output)
|
66
|
+
end
|
67
|
+
undef :render
|
68
|
+
|
69
|
+
end # module Base
|
70
|
+
include Base
|
71
|
+
end # class Renderer
|
72
|
+
end # module Alf
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Alf
|
2
|
+
class Renderer
|
3
|
+
module ClassMethods
|
4
|
+
|
5
|
+
#
|
6
|
+
# Returns registered renderers
|
7
|
+
#
|
8
|
+
def renderers
|
9
|
+
@renderers ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Register a renderering class with a given name and description.
|
14
|
+
#
|
15
|
+
# Registered class must at least provide a constructor with an empty
|
16
|
+
# signature. The name must be a symbol which can safely be used as a ruby
|
17
|
+
# method name. A factory class method of that name and degelation signature
|
18
|
+
# is automatically installed on the Renderer class.
|
19
|
+
#
|
20
|
+
# @param [Symbol] name a name for the output format
|
21
|
+
# @param [String] description an output format description (for 'alf show')
|
22
|
+
# @param [Class] clazz Renderer subclass used to render in this format
|
23
|
+
#
|
24
|
+
def register(name, description, clazz)
|
25
|
+
renderers << [name, description, clazz]
|
26
|
+
(class << self; self; end).
|
27
|
+
send(:define_method, name) do |*args|
|
28
|
+
clazz.new(*args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Returns a Renderer instance for the given output format name.
|
34
|
+
#
|
35
|
+
# @param [Symbol] name name of an output format previously registered
|
36
|
+
# @param [...] args other arguments to pass to the renderer constructor
|
37
|
+
# @return [Renderer] a Renderer instance, already wired if args are
|
38
|
+
# provided
|
39
|
+
#
|
40
|
+
def renderer(name, *args)
|
41
|
+
if r = renderers.find{|triple| triple[0] == name}
|
42
|
+
r[2].new(*args)
|
43
|
+
else
|
44
|
+
raise "No renderer registered for #{name}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Yields each (name,description,clazz) previously registered in turn
|
50
|
+
#
|
51
|
+
def each_renderer
|
52
|
+
renderers.each(&Proc.new)
|
53
|
+
end
|
54
|
+
|
55
|
+
end # module ClassMethods
|
56
|
+
extend(ClassMethods)
|
57
|
+
end # class Renderer
|
58
|
+
end # module Alf
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Alf
|
2
|
+
class Renderer
|
3
|
+
#
|
4
|
+
# Implements the Renderer contract through inspect
|
5
|
+
#
|
6
|
+
class Rash < Renderer
|
7
|
+
|
8
|
+
# (see Renderer#render)
|
9
|
+
def render(input, output)
|
10
|
+
input.each do |tuple|
|
11
|
+
output << Tools.to_ruby_literal(tuple) << "\n"
|
12
|
+
end
|
13
|
+
output
|
14
|
+
end
|
15
|
+
|
16
|
+
Renderer.register(:rash, "as ruby hashes", self)
|
17
|
+
end # class Rash
|
18
|
+
end # class Renderer
|
19
|
+
end # module Alf
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Alf
|
2
|
+
module Tools
|
3
|
+
|
4
|
+
|
5
|
+
# Helper to define methods with multiple signatures.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# varargs([1, "hello"], [Integer, String]) # => [1, "hello"]
|
10
|
+
# varargs(["hello"], [Integer, String]) # => [nil, "hello"]
|
11
|
+
#
|
12
|
+
def varargs(args, types)
|
13
|
+
types.collect{|t| t===args.first ? args.shift : nil}
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Attempt to require(who) the most friendly way as possible.
|
18
|
+
#
|
19
|
+
def friendly_require(who, dep = nil, retried = false)
|
20
|
+
gem(who, dep) if dep && defined?(Gem)
|
21
|
+
require who
|
22
|
+
rescue LoadError => ex
|
23
|
+
if retried
|
24
|
+
raise "Unable to require #{who}, which is now needed\n"\
|
25
|
+
"Try 'gem install #{who}'"
|
26
|
+
else
|
27
|
+
require 'rubygems' unless defined?(Gem)
|
28
|
+
friendly_require(who, dep, true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the unqualified name of a ruby class or module
|
33
|
+
#
|
34
|
+
# Example
|
35
|
+
#
|
36
|
+
# class_name(Alf::Tools) -> :Tools
|
37
|
+
#
|
38
|
+
def class_name(clazz)
|
39
|
+
clazz.name.to_s =~ /([A-Za-z0-9_]+)$/
|
40
|
+
$1.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Converts an unqualified class or module name to a ruby case method name.
|
45
|
+
#
|
46
|
+
# Example
|
47
|
+
#
|
48
|
+
# ruby_case(:Alf) -> "alf"
|
49
|
+
# ruby_case(:HelloWorld) -> "hello_world"
|
50
|
+
#
|
51
|
+
def ruby_case(s)
|
52
|
+
s.to_s.gsub(/[A-Z]/){|x| "_#{x.downcase}"}[1..-1]
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Returns the first non nil values from arguments
|
57
|
+
#
|
58
|
+
# Example
|
59
|
+
#
|
60
|
+
# coalesce(nil, 1, "abc") -> 1
|
61
|
+
#
|
62
|
+
def coalesce(*args)
|
63
|
+
found = args.find{|x| !x.nil?}
|
64
|
+
found.nil? ? (block_given? ? yield : nil) : found
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Iterates over enum and yields the block on each element.
|
69
|
+
# Collect block results as key/value pairs returns them as
|
70
|
+
# a Hash.
|
71
|
+
#
|
72
|
+
def tuple_collect(enum)
|
73
|
+
Hash[enum.collect{|elm| yield(elm)}]
|
74
|
+
end
|
75
|
+
|
76
|
+
end # module Tools
|
77
|
+
end # module Alf
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Alf
|
2
|
+
module Tools
|
3
|
+
|
4
|
+
# Myrrha rules for converting to ruby literals
|
5
|
+
ToLispy = Myrrha::coercions do |r|
|
6
|
+
|
7
|
+
# Delegate to #to_lispy if it exists
|
8
|
+
lispy_able = lambda{|v,rd| v.respond_to?(:to_lispy)}
|
9
|
+
r.upon(lispy_able) do |v,rd|
|
10
|
+
v.to_lispy
|
11
|
+
end
|
12
|
+
|
13
|
+
# On AttrList
|
14
|
+
r.upon(Types::AttrList) do |v, rd|
|
15
|
+
Tools.to_ruby_literal(v.attributes)
|
16
|
+
end
|
17
|
+
|
18
|
+
# On Heading
|
19
|
+
r.upon(Types::Heading) do |v, rd|
|
20
|
+
Tools.to_ruby_literal(v.attributes)
|
21
|
+
end
|
22
|
+
|
23
|
+
# On Ordering
|
24
|
+
r.upon(Types::Ordering) do |v, rd|
|
25
|
+
Tools.to_ruby_literal(v.ordering)
|
26
|
+
end
|
27
|
+
|
28
|
+
# On Renaming
|
29
|
+
r.upon(Types::Renaming) do |v, rd|
|
30
|
+
Tools.to_ruby_literal(v.renaming)
|
31
|
+
end
|
32
|
+
|
33
|
+
# On Renaming
|
34
|
+
r.upon(lambda{|v,rd| Iterator::Proxy === v}) do |v, rd|
|
35
|
+
Tools.to_ruby_literal(v.dataset)
|
36
|
+
end
|
37
|
+
|
38
|
+
# On TupleExpression
|
39
|
+
r.upon(Types::TupleExpression) do |v, rd|
|
40
|
+
unless src = v.source
|
41
|
+
raise NotImplementedError, "TupleExpression #{v} has no source"
|
42
|
+
end
|
43
|
+
"->(){ #{src} }"
|
44
|
+
end
|
45
|
+
|
46
|
+
# On TuplePredicate
|
47
|
+
r.upon(Types::TuplePredicate) do |v, rd|
|
48
|
+
unless src = v.source
|
49
|
+
raise NotImplementedError, "TuplePredicate #{v} has no source"
|
50
|
+
end
|
51
|
+
"->(){ #{src} }"
|
52
|
+
end
|
53
|
+
|
54
|
+
# On TupleComputation
|
55
|
+
r.upon(Types::TupleComputation) do |v, rd|
|
56
|
+
"{" + v.computation.collect{|name,compu|
|
57
|
+
[name.inspect, r.coerce(compu)].join(" => ")
|
58
|
+
}.join(', ') + "}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# On Aggregator
|
62
|
+
r.upon(lambda{|v,_| Aggregator === v}) do |v, rd|
|
63
|
+
unless src = v.source
|
64
|
+
raise NotImplementedError, "Aggregator #{v} has no source"
|
65
|
+
end
|
66
|
+
src
|
67
|
+
end
|
68
|
+
|
69
|
+
# On Summarization
|
70
|
+
r.upon(Types::Summarization) do |v, rd|
|
71
|
+
"{" + v.aggregations.collect{|name,compu|
|
72
|
+
[name.inspect, r.coerce(compu)].join(" => ")
|
73
|
+
}.join(', ') + "}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# On Command and Operator
|
77
|
+
cmd = lambda{|v,_| (Command === v) || (Operator === v)}
|
78
|
+
r.upon(cmd) do |v,rd|
|
79
|
+
cmdname = v.class.command_name.to_s.gsub('-', '_')
|
80
|
+
oper, args, opts = v.class.signature.collect_on(v)
|
81
|
+
args = opts.empty? ? (oper + args) : (oper + args + [ opts ])
|
82
|
+
args = args.collect{|arg| r.coerce(arg)}
|
83
|
+
"(#{cmdname} #{args.join(', ')})"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Let's assume to to_ruby_literal will make the job
|
87
|
+
r.fallback(Object) do |v, _|
|
88
|
+
Tools.to_ruby_literal(v)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
# Delegated to ToLispy
|
94
|
+
def to_lispy(value)
|
95
|
+
ToLispy.apply(value)
|
96
|
+
end
|
97
|
+
|
98
|
+
end # module Tools
|
99
|
+
end # module Alf
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Alf
|
2
|
+
module Tools
|
3
|
+
|
4
|
+
# Myrrha rules for converting to ruby literals
|
5
|
+
ToRubyLiteral = Myrrha::ToRubyLiteral.dup.append do
|
6
|
+
end
|
7
|
+
|
8
|
+
# Delegated to ToRubyLiteral
|
9
|
+
def to_ruby_literal(value)
|
10
|
+
ToRubyLiteral.apply(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
end # module Tools
|
14
|
+
end # module Alf
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Alf
|
2
|
+
module Tools
|
3
|
+
#
|
4
|
+
# Provides a handle, implementing a flyweight design pattern on tuples.
|
5
|
+
#
|
6
|
+
class TupleHandle
|
7
|
+
|
8
|
+
# Creates an handle instance
|
9
|
+
def initialize
|
10
|
+
@tuple = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Sets the next tuple to use.
|
15
|
+
#
|
16
|
+
# This method installs the handle as a side effect
|
17
|
+
# on first call.
|
18
|
+
#
|
19
|
+
def set(tuple)
|
20
|
+
build(tuple) if @tuple.nil?
|
21
|
+
@tuple = tuple
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Evaluates a tuple expression on the current tuple.
|
27
|
+
#
|
28
|
+
def evaluate(expr)
|
29
|
+
TupleExpression.coerce(expr).evaluate(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
#
|
35
|
+
# Builds this handle with a tuple.
|
36
|
+
#
|
37
|
+
# This method should be called only once and installs
|
38
|
+
# instance methods on the handle with keys of _tuple_.
|
39
|
+
#
|
40
|
+
def build(tuple)
|
41
|
+
tuple.keys.each do |k|
|
42
|
+
(class << self; self; end).send(:define_method, k) do
|
43
|
+
@tuple[k]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class TupleHandle
|
49
|
+
end # module Tools
|
50
|
+
end # module Alf
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Alf
|
2
|
+
module Types
|
3
|
+
#
|
4
|
+
# Defines a projection key
|
5
|
+
#
|
6
|
+
class AttrList
|
7
|
+
|
8
|
+
# Projection attributes
|
9
|
+
attr_accessor :attributes
|
10
|
+
|
11
|
+
def initialize(attributes)
|
12
|
+
@attributes = attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.coerce(arg)
|
16
|
+
case arg
|
17
|
+
when AttrList
|
18
|
+
arg
|
19
|
+
when Ordering
|
20
|
+
AttrList.new(arg.attributes)
|
21
|
+
when Array
|
22
|
+
AttrList.new(arg.collect{|s| Tools.coerce(s, AttrName)})
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Unable to coerce #{arg} to a projection key"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.from_argv(argv, opts = {})
|
29
|
+
coerce(argv)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_ordering
|
33
|
+
Ordering.new attributes.collect{|arg| [arg, :asc]}
|
34
|
+
end
|
35
|
+
|
36
|
+
def project(tuple, allbut = false)
|
37
|
+
split(tuple, allbut).first
|
38
|
+
end
|
39
|
+
|
40
|
+
def split(tuple, allbut = false)
|
41
|
+
projection, rest = {}, tuple.dup
|
42
|
+
attributes.each do |a|
|
43
|
+
projection[a] = tuple[a]
|
44
|
+
rest.delete(a)
|
45
|
+
end
|
46
|
+
allbut ? [rest, projection] : [projection, rest]
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
other.is_a?(AttrList) && (other.attributes == attributes)
|
51
|
+
end
|
52
|
+
alias :eql? :==
|
53
|
+
|
54
|
+
end # class AttrList
|
55
|
+
end # module Types
|
56
|
+
end # module Alf
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Alf
|
2
|
+
module Types
|
3
|
+
#
|
4
|
+
# Data type for being a valid attribute name
|
5
|
+
#
|
6
|
+
class AttrName < Symbol
|
7
|
+
extend Myrrha::Domain
|
8
|
+
|
9
|
+
def self.predicate
|
10
|
+
@predicate ||= lambda{|s| s.to_s =~ /^[a-zA-Z0-9_]+$/}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.coerce(arg)
|
14
|
+
if arg.respond_to?(:to_sym)
|
15
|
+
sym = arg.to_sym
|
16
|
+
return sym if self.===(sym)
|
17
|
+
end
|
18
|
+
raise ArgumentError, "Unable to coerce `#{arg.inspect}` to AttrName"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_argv(argv, opts = {})
|
22
|
+
raise ArgumentError if argv.size > 1
|
23
|
+
coerce(argv.first || opts[:default])
|
24
|
+
end
|
25
|
+
|
26
|
+
end # class AttrName
|
27
|
+
end # module Types
|
28
|
+
end # module Alf
|