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,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
|