alf 0.12.2 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +186 -80
- data/Gemfile +16 -32
- data/Gemfile.lock +35 -34
- data/LICENCE.md +1 -1
- data/Manifest.txt +7 -9
- data/README.md +139 -809
- data/alf.gemspec +6 -15
- data/alf.noespec +16 -34
- data/lib/alf.rb +3 -73
- data/lib/alf/loader.rb +3 -3
- data/lib/alf/version.rb +2 -2
- data/spec/spec_helper.rb +3 -18
- data/spec/test_alf.rb +8 -0
- data/tasks/test.rake +6 -0
- data/test/migrations/test_folder_migration.rb +18 -0
- data/test/migrations/test_sequel_migration.rb +34 -0
- data/test/seeding/test_seeding.rb +49 -0
- data/test/test_alf.rb +8 -0
- data/test/test_helpers.rb +24 -0
- metadata +54 -952
- data/TODO.md +0 -21
- data/bin/alf +0 -27
- data/doc/commands/exec.md +0 -16
- data/doc/commands/help.md +0 -11
- data/doc/commands/main.md +0 -33
- data/doc/commands/show.md +0 -19
- data/doc/operators/non_relational/autonum.md +0 -23
- data/doc/operators/non_relational/clip.md +0 -31
- data/doc/operators/non_relational/coerce.md +0 -15
- data/doc/operators/non_relational/compact.md +0 -20
- data/doc/operators/non_relational/defaults.md +0 -32
- data/doc/operators/non_relational/generator.md +0 -20
- data/doc/operators/non_relational/sort.md +0 -24
- data/doc/operators/relational/extend.md +0 -18
- data/doc/operators/relational/group.md +0 -27
- data/doc/operators/relational/heading.md +0 -20
- data/doc/operators/relational/intersect.md +0 -13
- data/doc/operators/relational/join.md +0 -28
- data/doc/operators/relational/matching.md +0 -24
- data/doc/operators/relational/minus.md +0 -12
- data/doc/operators/relational/not-matching.md +0 -20
- data/doc/operators/relational/project.md +0 -28
- data/doc/operators/relational/quota.md +0 -21
- data/doc/operators/relational/rank.md +0 -27
- data/doc/operators/relational/rename.md +0 -17
- data/doc/operators/relational/restrict.md +0 -25
- data/doc/operators/relational/summarize.md +0 -25
- data/doc/operators/relational/ungroup.md +0 -20
- data/doc/operators/relational/union.md +0 -14
- data/doc/operators/relational/unwrap.md +0 -20
- data/doc/operators/relational/wrap.md +0 -24
- data/examples/csv/suppliers.csv +0 -6
- data/examples/logs/access.log +0 -1000
- data/examples/logs/combined.alf +0 -2
- data/examples/logs/hits.alf +0 -14
- data/examples/logs/not_found.alf +0 -7
- data/examples/logs/robots-cheating.alf +0 -11
- data/examples/logs/robots.alf +0 -8
- data/examples/operators/autonum.alf +0 -6
- data/examples/operators/cities.rash +0 -4
- data/examples/operators/clip.alf +0 -3
- data/examples/operators/compact.alf +0 -2
- data/examples/operators/database.alf +0 -5
- data/examples/operators/defaults.alf +0 -3
- data/examples/operators/extend.alf +0 -3
- data/examples/operators/group.alf +0 -3
- data/examples/operators/intersect.alf +0 -4
- data/examples/operators/join.alf +0 -2
- data/examples/operators/matching.alf +0 -2
- data/examples/operators/minus.alf +0 -8
- data/examples/operators/not_matching.alf +0 -2
- data/examples/operators/nulls.rash +0 -3
- data/examples/operators/parts.rash +0 -6
- data/examples/operators/project.alf +0 -3
- data/examples/operators/pseudo-with.alf +0 -7
- data/examples/operators/quota.alf +0 -4
- data/examples/operators/rank.alf +0 -4
- data/examples/operators/rename.alf +0 -3
- data/examples/operators/restrict.alf +0 -2
- data/examples/operators/schema.yaml +0 -28
- data/examples/operators/sort.alf +0 -4
- data/examples/operators/summarize.alf +0 -16
- data/examples/operators/suppliers.rash +0 -5
- data/examples/operators/supplies.rash +0 -12
- data/examples/operators/ungroup.alf +0 -4
- data/examples/operators/union.alf +0 -3
- data/examples/operators/unwrap.alf +0 -4
- data/examples/operators/wrap.alf +0 -2
- data/lib/alf-csv/alf/csv.rb +0 -3
- data/lib/alf-csv/alf/csv/commons.rb +0 -36
- data/lib/alf-csv/alf/csv/reader.rb +0 -33
- data/lib/alf-csv/alf/csv/renderer.rb +0 -38
- data/lib/alf-engine/alf/engine.rb +0 -25
- data/lib/alf-engine/alf/engine/aggregate.rb +0 -44
- data/lib/alf-engine/alf/engine/autonum.rb +0 -45
- data/lib/alf-engine/alf/engine/cesure.rb +0 -45
- data/lib/alf-engine/alf/engine/clip.rb +0 -53
- data/lib/alf-engine/alf/engine/coerce.rb +0 -46
- data/lib/alf-engine/alf/engine/cog.rb +0 -7
- data/lib/alf-engine/alf/engine/compact.rb +0 -26
- data/lib/alf-engine/alf/engine/compact/set.rb +0 -23
- data/lib/alf-engine/alf/engine/compact/uniq.rb +0 -23
- data/lib/alf-engine/alf/engine/concat.rb +0 -25
- data/lib/alf-engine/alf/engine/defaults.rb +0 -43
- data/lib/alf-engine/alf/engine/filter.rb +0 -41
- data/lib/alf-engine/alf/engine/generator.rb +0 -50
- data/lib/alf-engine/alf/engine/group.rb +0 -7
- data/lib/alf-engine/alf/engine/group/hash.rb +0 -40
- data/lib/alf-engine/alf/engine/join.rb +0 -7
- data/lib/alf-engine/alf/engine/join/hash.rb +0 -35
- data/lib/alf-engine/alf/engine/materialize.rb +0 -8
- data/lib/alf-engine/alf/engine/materialize/array.rb +0 -78
- data/lib/alf-engine/alf/engine/materialize/hash.rb +0 -122
- data/lib/alf-engine/alf/engine/quota.rb +0 -7
- data/lib/alf-engine/alf/engine/quota/cesure.rb +0 -46
- data/lib/alf-engine/alf/engine/rank.rb +0 -7
- data/lib/alf-engine/alf/engine/rank/cesure.rb +0 -48
- data/lib/alf-engine/alf/engine/rename.rb +0 -39
- data/lib/alf-engine/alf/engine/semi.rb +0 -7
- data/lib/alf-engine/alf/engine/semi/hash.rb +0 -39
- data/lib/alf-engine/alf/engine/set_attr.rb +0 -46
- data/lib/alf-engine/alf/engine/sort.rb +0 -28
- data/lib/alf-engine/alf/engine/sort/in_memory.rb +0 -39
- data/lib/alf-engine/alf/engine/summarize.rb +0 -8
- data/lib/alf-engine/alf/engine/summarize/cesure.rb +0 -51
- data/lib/alf-engine/alf/engine/summarize/hash.rb +0 -35
- data/lib/alf-engine/alf/engine/ungroup.rb +0 -29
- data/lib/alf-engine/alf/engine/unwrap.rb +0 -31
- data/lib/alf-engine/alf/engine/wrap.rb +0 -39
- data/lib/alf-logs/alf/logs.rb +0 -1
- data/lib/alf-logs/alf/logs/reader.rb +0 -98
- data/lib/alf-sequel/alf/sequel.rb +0 -2
- data/lib/alf-sequel/alf/sequel/environment.rb +0 -61
- data/lib/alf-sequel/alf/sequel/iterator.rb +0 -21
- data/lib/alf-shell/alf/shell.rb +0 -40
- data/lib/alf-shell/alf/shell/command.rb +0 -26
- data/lib/alf-shell/alf/shell/command/exec.rb +0 -11
- data/lib/alf-shell/alf/shell/command/help.rb +0 -30
- data/lib/alf-shell/alf/shell/command/main.rb +0 -136
- data/lib/alf-shell/alf/shell/command/main/class_methods.rb +0 -46
- data/lib/alf-shell/alf/shell/command/show.rb +0 -63
- data/lib/alf-shell/alf/shell/doc_manager.rb +0 -72
- data/lib/alf-shell/alf/shell/operator.rb +0 -86
- data/lib/alf-yaml/alf/yaml.rb +0 -1
- data/lib/alf-yaml/alf/yaml/renderer.rb +0 -22
- data/lib/alf/aggregator.rb +0 -51
- data/lib/alf/aggregator/avg.rb +0 -39
- data/lib/alf/aggregator/class_methods.rb +0 -77
- data/lib/alf/aggregator/collect.rb +0 -32
- data/lib/alf/aggregator/concat.rb +0 -47
- data/lib/alf/aggregator/count.rb +0 -32
- data/lib/alf/aggregator/instance_methods.rb +0 -119
- data/lib/alf/aggregator/max.rb +0 -32
- data/lib/alf/aggregator/min.rb +0 -32
- data/lib/alf/aggregator/stddev.rb +0 -25
- data/lib/alf/aggregator/sum.rb +0 -32
- data/lib/alf/aggregator/variance.rb +0 -45
- data/lib/alf/environment.rb +0 -34
- data/lib/alf/environment/class_methods.rb +0 -95
- data/lib/alf/environment/folder.rb +0 -60
- data/lib/alf/environment/instance_methods.rb +0 -26
- data/lib/alf/errors.rb +0 -5
- data/lib/alf/ext.rb +0 -4
- data/lib/alf/ext/relation.rb +0 -4
- data/lib/alf/iterator.rb +0 -28
- data/lib/alf/iterator/class_methods.rb +0 -20
- data/lib/alf/iterator/proxy.rb +0 -27
- data/lib/alf/lispy.rb +0 -24
- data/lib/alf/lispy/instance_methods.rb +0 -157
- data/lib/alf/operator.rb +0 -68
- data/lib/alf/operator/binary.rb +0 -21
- data/lib/alf/operator/class_methods.rb +0 -82
- data/lib/alf/operator/experimental.rb +0 -9
- data/lib/alf/operator/instance_methods.rb +0 -29
- data/lib/alf/operator/non_relational/autonum.rb +0 -19
- data/lib/alf/operator/non_relational/clip.rb +0 -20
- data/lib/alf/operator/non_relational/coerce.rb +0 -19
- data/lib/alf/operator/non_relational/compact.rb +0 -18
- data/lib/alf/operator/non_relational/defaults.rb +0 -22
- data/lib/alf/operator/non_relational/generator.rb +0 -20
- data/lib/alf/operator/non_relational/sort.rb +0 -19
- data/lib/alf/operator/nullary.rb +0 -11
- data/lib/alf/operator/relational/extend.rb +0 -19
- data/lib/alf/operator/relational/group.rb +0 -21
- data/lib/alf/operator/relational/heading.rb +0 -20
- data/lib/alf/operator/relational/intersect.rb +0 -18
- data/lib/alf/operator/relational/join.rb +0 -18
- data/lib/alf/operator/relational/matching.rb +0 -18
- data/lib/alf/operator/relational/minus.rb +0 -18
- data/lib/alf/operator/relational/not_matching.rb +0 -18
- data/lib/alf/operator/relational/project.rb +0 -22
- data/lib/alf/operator/relational/quota.rb +0 -23
- data/lib/alf/operator/relational/rank.rb +0 -22
- data/lib/alf/operator/relational/rename.rb +0 -18
- data/lib/alf/operator/relational/restrict.rb +0 -18
- data/lib/alf/operator/relational/summarize.rb +0 -27
- data/lib/alf/operator/relational/ungroup.rb +0 -19
- data/lib/alf/operator/relational/union.rb +0 -20
- data/lib/alf/operator/relational/unwrap.rb +0 -19
- data/lib/alf/operator/relational/wrap.rb +0 -20
- data/lib/alf/operator/signature.rb +0 -233
- data/lib/alf/operator/unary.rb +0 -16
- data/lib/alf/reader.rb +0 -37
- data/lib/alf/reader/alf_file.rb +0 -24
- data/lib/alf/reader/class_methods.rb +0 -91
- data/lib/alf/reader/instance_methods.rb +0 -97
- data/lib/alf/reader/rash.rb +0 -28
- data/lib/alf/relation.rb +0 -28
- data/lib/alf/relation/class_methods.rb +0 -28
- data/lib/alf/relation/instance_methods.rb +0 -119
- data/lib/alf/renderer.rb +0 -32
- data/lib/alf/renderer/class_methods.rb +0 -58
- data/lib/alf/renderer/instance_methods.rb +0 -55
- data/lib/alf/renderer/rash.rb +0 -33
- data/lib/alf/renderer/text.rb +0 -198
- data/lib/alf/tools.rb +0 -15
- data/lib/alf/tools/coerce.rb +0 -25
- data/lib/alf/tools/miscellaneous.rb +0 -111
- data/lib/alf/tools/to_lispy.rb +0 -96
- data/lib/alf/tools/to_relation.rb +0 -54
- data/lib/alf/tools/to_ruby_literal.rb +0 -31
- data/lib/alf/tools/tuple_handle.rb +0 -60
- data/lib/alf/types.rb +0 -49
- data/lib/alf/types/attr_list.rb +0 -160
- data/lib/alf/types/attr_name.rb +0 -66
- data/lib/alf/types/boolean.rb +0 -25
- data/lib/alf/types/heading.rb +0 -135
- data/lib/alf/types/ordering.rb +0 -146
- data/lib/alf/types/renaming.rb +0 -97
- data/lib/alf/types/size.rb +0 -57
- data/lib/alf/types/summarization.rb +0 -138
- data/lib/alf/types/tuple_computation.rb +0 -136
- data/lib/alf/types/tuple_expression.rb +0 -152
- data/lib/alf/types/tuple_predicate.rb +0 -73
- data/spec/integration/__database__/group.alf +0 -3
- data/spec/integration/__database__/parts.rash +0 -6
- data/spec/integration/__database__/suppliers.rash +0 -5
- data/spec/integration/__database__/suppliers_csv.csv +0 -6
- data/spec/integration/__database__/supplies.rash +0 -12
- data/spec/integration/alf/example.rash +0 -1
- data/spec/integration/alf/test_Relation.rb +0 -32
- data/spec/integration/ext/test_relation.rb +0 -16
- data/spec/integration/lispy/test_relation.rb +0 -37
- data/spec/integration/lispy/test_run.rb +0 -40
- data/spec/integration/lispy/test_tuple.rb +0 -36
- data/spec/integration/semantics/test_join.alf +0 -9
- data/spec/integration/semantics/test_minus.alf +0 -5
- data/spec/integration/semantics/test_project.alf +0 -8
- data/spec/integration/semantics/test_rank.alf +0 -34
- data/spec/integration/shell/alf/alf.db +0 -0
- data/spec/integration/shell/alf/alf_e.cmd +0 -1
- data/spec/integration/shell/alf/alf_e.stdout +0 -4
- data/spec/integration/shell/alf/alf_env.cmd +0 -1
- data/spec/integration/shell/alf/alf_env.stdout +0 -5
- data/spec/integration/shell/alf/alf_env_sqlite.cmd +0 -1
- data/spec/integration/shell/alf/alf_env_sqlite.stdout +0 -9
- data/spec/integration/shell/alf/alf_help.cmd +0 -1
- data/spec/integration/shell/alf/alf_help.stdout +0 -69
- data/spec/integration/shell/alf/alf_implicit.alf +0 -1
- data/spec/integration/shell/alf/alf_implicit_exec.cmd +0 -1
- data/spec/integration/shell/alf/alf_implicit_exec.stdout +0 -4
- data/spec/integration/shell/alf/alf_r.cmd +0 -1
- data/spec/integration/shell/alf/alf_r.stdout +0 -5
- data/spec/integration/shell/alf/alf_version.cmd +0 -1
- data/spec/integration/shell/alf/alf_version.stdout +0 -2
- data/spec/integration/shell/alf/alf_yaml.cmd +0 -1
- data/spec/integration/shell/alf/alf_yaml.stdout +0 -22
- data/spec/integration/shell/alf/rel.rash +0 -1
- data/spec/integration/shell/autonum/autonum_0.cmd +0 -1
- data/spec/integration/shell/autonum/autonum_0.stdout +0 -9
- data/spec/integration/shell/autonum/autonum_1.cmd +0 -1
- data/spec/integration/shell/autonum/autonum_1.stdout +0 -9
- data/spec/integration/shell/clip/clip_0.cmd +0 -1
- data/spec/integration/shell/clip/clip_0.stdout +0 -9
- data/spec/integration/shell/clip/clip_1.cmd +0 -1
- data/spec/integration/shell/clip/clip_1.stdout +0 -9
- data/spec/integration/shell/coerce/coerce_1.cmd +0 -1
- data/spec/integration/shell/coerce/coerce_1.stdout +0 -5
- data/spec/integration/shell/compact/compact_0.cmd +0 -1
- data/spec/integration/shell/compact/compact_0.stdout +0 -9
- data/spec/integration/shell/defaults/defaults_0.cmd +0 -1
- data/spec/integration/shell/defaults/defaults_0.stdout +0 -9
- data/spec/integration/shell/defaults/defaults_1.cmd +0 -1
- data/spec/integration/shell/defaults/defaults_1.stdout +0 -9
- data/spec/integration/shell/defaults/defaults_2.cmd +0 -1
- data/spec/integration/shell/defaults/defaults_2.stdout +0 -9
- data/spec/integration/shell/extend/extend_0.cmd +0 -1
- data/spec/integration/shell/extend/extend_0.stdout +0 -16
- data/spec/integration/shell/generator/generator_1.cmd +0 -1
- data/spec/integration/shell/generator/generator_1.stdout +0 -10
- data/spec/integration/shell/generator/generator_2.cmd +0 -1
- data/spec/integration/shell/generator/generator_2.stdout +0 -5
- data/spec/integration/shell/generator/generator_3.cmd +0 -1
- data/spec/integration/shell/generator/generator_3.stdout +0 -5
- data/spec/integration/shell/group/group_0.cmd +0 -1
- data/spec/integration/shell/group/group_0.stdout +0 -32
- data/spec/integration/shell/group/group_1.cmd +0 -1
- data/spec/integration/shell/group/group_1.stdout +0 -32
- data/spec/integration/shell/help/help_1.cmd +0 -1
- data/spec/integration/shell/help/help_1.stdout +0 -24
- data/spec/integration/shell/intersect/intersect_0.cmd +0 -1
- data/spec/integration/shell/intersect/intersect_0.stdout +0 -9
- data/spec/integration/shell/join/join_0.cmd +0 -1
- data/spec/integration/shell/join/join_0.stdout +0 -16
- data/spec/integration/shell/matching/matching_0.cmd +0 -1
- data/spec/integration/shell/matching/matching_0.stdout +0 -8
- data/spec/integration/shell/minus/minus_0.cmd +0 -1
- data/spec/integration/shell/minus/minus_0.stdout +0 -4
- data/spec/integration/shell/not-matching/not-matching_0.cmd +0 -1
- data/spec/integration/shell/not-matching/not-matching_0.stdout +0 -5
- data/spec/integration/shell/project/project_0.cmd +0 -1
- data/spec/integration/shell/project/project_0.stdout +0 -9
- data/spec/integration/shell/project/project_1.cmd +0 -1
- data/spec/integration/shell/project/project_1.stdout +0 -9
- data/spec/integration/shell/quota/quota_0.cmd +0 -1
- data/spec/integration/shell/quota/quota_0.stdout +0 -16
- data/spec/integration/shell/rank/rank_1.cmd +0 -1
- data/spec/integration/shell/rank/rank_1.stdout +0 -10
- data/spec/integration/shell/rank/rank_2.cmd +0 -1
- data/spec/integration/shell/rank/rank_2.stdout +0 -10
- data/spec/integration/shell/rank/rank_3.cmd +0 -1
- data/spec/integration/shell/rank/rank_3.stdout +0 -10
- data/spec/integration/shell/rank/rank_4.cmd +0 -1
- data/spec/integration/shell/rank/rank_4.stdout +0 -6
- data/spec/integration/shell/rank/rank_5.cmd +0 -1
- data/spec/integration/shell/rank/rank_5.stdout +0 -6
- data/spec/integration/shell/rename/rename_0.cmd +0 -1
- data/spec/integration/shell/rename/rename_0.stdout +0 -9
- data/spec/integration/shell/restrict/restrict_0.cmd +0 -1
- data/spec/integration/shell/restrict/restrict_0.stdout +0 -6
- data/spec/integration/shell/restrict/restrict_1.cmd +0 -1
- data/spec/integration/shell/restrict/restrict_1.stdout +0 -6
- data/spec/integration/shell/show/show_base.cmd +0 -1
- data/spec/integration/shell/show/show_base.stdout +0 -9
- data/spec/integration/shell/show/show_base_sort_1.cmd +0 -1
- data/spec/integration/shell/show/show_base_sort_1.stdout +0 -9
- data/spec/integration/shell/show/show_base_sort_2.cmd +0 -1
- data/spec/integration/shell/show/show_base_sort_2.stdout +0 -9
- data/spec/integration/shell/show/show_conflictual.cmd +0 -1
- data/spec/integration/shell/show/show_conflictual.stdout +0 -5
- data/spec/integration/shell/show/show_csv.cmd +0 -1
- data/spec/integration/shell/show/show_csv.stdout +0 -6
- data/spec/integration/shell/show/show_ff.cmd +0 -1
- data/spec/integration/shell/show/show_ff.stdout +0 -10
- data/spec/integration/shell/show/show_rash.cmd +0 -1
- data/spec/integration/shell/show/show_rash.stdout +0 -5
- data/spec/integration/shell/show/show_rash_pretty.cmd +0 -1
- data/spec/integration/shell/show/show_rash_pretty.stdout +0 -30
- data/spec/integration/shell/show/show_yaml.cmd +0 -1
- data/spec/integration/shell/show/show_yaml.stdout +0 -22
- data/spec/integration/shell/sort/sort_0.cmd +0 -1
- data/spec/integration/shell/sort/sort_0.stdout +0 -9
- data/spec/integration/shell/sort/sort_1.cmd +0 -1
- data/spec/integration/shell/sort/sort_1.stdout +0 -9
- data/spec/integration/shell/sort/sort_2.cmd +0 -1
- data/spec/integration/shell/sort/sort_2.stdout +0 -9
- data/spec/integration/shell/sort/sort_3.cmd +0 -1
- data/spec/integration/shell/sort/sort_3.stdout +0 -9
- data/spec/integration/shell/summarize/summarize_0.cmd +0 -1
- data/spec/integration/shell/summarize/summarize_0.stdout +0 -8
- data/spec/integration/shell/ungroup/ungroup_0.cmd +0 -1
- data/spec/integration/shell/ungroup/ungroup_0.stdout +0 -16
- data/spec/integration/shell/union/union_0.cmd +0 -1
- data/spec/integration/shell/union/union_0.stdout +0 -9
- data/spec/integration/shell/unwrap/unwrap_0.cmd +0 -1
- data/spec/integration/shell/unwrap/unwrap_0.stdout +0 -9
- data/spec/integration/shell/wrap/wrap_0.cmd +0 -1
- data/spec/integration/shell/wrap/wrap_0.stdout +0 -9
- data/spec/integration/test_alf.rb +0 -34
- data/spec/integration/test_examples.rb +0 -15
- data/spec/integration/test_lispy.rb +0 -31
- data/spec/integration/test_semantics.rb +0 -40
- data/spec/integration/test_shell.rb +0 -47
- data/spec/regression/alf_file/__FILE__.alf +0 -2
- data/spec/regression/alf_file/suppliers.rash +0 -5
- data/spec/regression/alf_file/test___FILE__.rb +0 -17
- data/spec/regression/heading/test_heading_with_date.rb +0 -12
- data/spec/regression/lispy/test_compile.rb +0 -14
- data/spec/regression/logs/apache_combined.log +0 -5
- data/spec/regression/logs/test_path_attribute.rb +0 -25
- data/spec/regression/relation/test_relation_allbut_all.rb +0 -14
- data/spec/regression/relation/test_relation_with_date.rb +0 -12
- data/spec/regression/restrict/test_restrict_with_keywords.rb +0 -17
- data/spec/shared/a_valid_type_implementation.rb +0 -47
- data/spec/shared/a_value.rb +0 -12
- data/spec/shared/an_operator_class.rb +0 -36
- data/spec/unit/alf-core/aggregator/test_avg.rb +0 -22
- data/spec/unit/alf-core/aggregator/test_collect.rb +0 -25
- data/spec/unit/alf-core/aggregator/test_concat.rb +0 -31
- data/spec/unit/alf-core/aggregator/test_count.rb +0 -17
- data/spec/unit/alf-core/aggregator/test_max.rb +0 -23
- data/spec/unit/alf-core/aggregator/test_min.rb +0 -23
- data/spec/unit/alf-core/aggregator/test_stddev.rb +0 -27
- data/spec/unit/alf-core/aggregator/test_sum.rb +0 -23
- data/spec/unit/alf-core/aggregator/test_variance.rb +0 -29
- data/spec/unit/alf-core/assumptions/test_file.rb +0 -17
- data/spec/unit/alf-core/assumptions/test_instance_eval.rb +0 -15
- data/spec/unit/alf-core/assumptions/test_scoping.rb +0 -29
- data/spec/unit/alf-core/assumptions/test_set.rb +0 -64
- data/spec/unit/alf-core/environment/examples/suppliers.rash +0 -5
- data/spec/unit/alf-core/environment/test_folder.rb +0 -53
- data/spec/unit/alf-core/operator/non_relational/test_autonum.rb +0 -48
- data/spec/unit/alf-core/operator/non_relational/test_clip.rb +0 -35
- data/spec/unit/alf-core/operator/non_relational/test_coerce.rb +0 -29
- data/spec/unit/alf-core/operator/non_relational/test_compact.rb +0 -32
- data/spec/unit/alf-core/operator/non_relational/test_defaults.rb +0 -53
- data/spec/unit/alf-core/operator/non_relational/test_generator.rb +0 -60
- data/spec/unit/alf-core/operator/non_relational/test_sort.rb +0 -60
- data/spec/unit/alf-core/operator/relational/test_extend.rb +0 -28
- data/spec/unit/alf-core/operator/relational/test_group.rb +0 -39
- data/spec/unit/alf-core/operator/relational/test_heading.rb +0 -27
- data/spec/unit/alf-core/operator/relational/test_intersect.rb +0 -58
- data/spec/unit/alf-core/operator/relational/test_join.rb +0 -36
- data/spec/unit/alf-core/operator/relational/test_minus.rb +0 -56
- data/spec/unit/alf-core/operator/relational/test_project.rb +0 -62
- data/spec/unit/alf-core/operator/relational/test_quota.rb +0 -36
- data/spec/unit/alf-core/operator/relational/test_rank.rb +0 -48
- data/spec/unit/alf-core/operator/relational/test_rename.rb +0 -26
- data/spec/unit/alf-core/operator/relational/test_restrict.rb +0 -45
- data/spec/unit/alf-core/operator/relational/test_summarize.rb +0 -44
- data/spec/unit/alf-core/operator/relational/test_ungroup.rb +0 -29
- data/spec/unit/alf-core/operator/relational/test_union.rb +0 -35
- data/spec/unit/alf-core/operator/relational/test_unwrap.rb +0 -26
- data/spec/unit/alf-core/operator/relational/test_wrap.rb +0 -26
- data/spec/unit/alf-core/operator/signature/test_argv2args.rb +0 -82
- data/spec/unit/alf-core/operator/signature/test_collect_on.rb +0 -38
- data/spec/unit/alf-core/operator/signature/test_initialize.rb +0 -17
- data/spec/unit/alf-core/operator/signature/test_install.rb +0 -56
- data/spec/unit/alf-core/operator/signature/test_option_parser.rb +0 -36
- data/spec/unit/alf-core/operator/signature/test_parse_args.rb +0 -90
- data/spec/unit/alf-core/operator/signature/test_to_lispy.rb +0 -102
- data/spec/unit/alf-core/operator/signature/test_to_shell.rb +0 -103
- data/spec/unit/alf-core/operator/test_non_relational.rb +0 -20
- data/spec/unit/alf-core/operator/test_relational.rb +0 -31
- data/spec/unit/alf-core/reader/input.rb +0 -2
- data/spec/unit/alf-core/reader/test_alf_file.rb +0 -30
- data/spec/unit/alf-core/reader/test_initialize.rb +0 -60
- data/spec/unit/alf-core/reader/test_looks_a_path.rb +0 -20
- data/spec/unit/alf-core/reader/test_rash.rb +0 -31
- data/spec/unit/alf-core/relation/test_coerce.rb +0 -53
- data/spec/unit/alf-core/relation/test_inspect.rb +0 -20
- data/spec/unit/alf-core/relation/test_relops.rb +0 -55
- data/spec/unit/alf-core/relation/test_to_a.rb +0 -41
- data/spec/unit/alf-core/renderer/test_initialize.rb +0 -60
- data/spec/unit/alf-core/test_aggregator.rb +0 -58
- data/spec/unit/alf-core/test_environment.rb +0 -30
- data/spec/unit/alf-core/test_operator.rb +0 -16
- data/spec/unit/alf-core/test_reader.rb +0 -40
- data/spec/unit/alf-core/test_relation.rb +0 -75
- data/spec/unit/alf-core/test_renderer.rb +0 -53
- data/spec/unit/alf-core/text/test_cell.rb +0 -35
- data/spec/unit/alf-core/text/test_row.rb +0 -30
- data/spec/unit/alf-core/text/test_table.rb +0 -39
- data/spec/unit/alf-core/tools/test_class_name.rb +0 -16
- data/spec/unit/alf-core/tools/test_coalesce.rb +0 -19
- data/spec/unit/alf-core/tools/test_coerce.rb +0 -16
- data/spec/unit/alf-core/tools/test_ruby_case.rb +0 -16
- data/spec/unit/alf-core/tools/test_to_lispy.rb +0 -145
- data/spec/unit/alf-core/tools/test_to_relation.rb +0 -39
- data/spec/unit/alf-core/tools/test_to_ruby_literal.rb +0 -10
- data/spec/unit/alf-core/tools/test_tuple_handle.rb +0 -52
- data/spec/unit/alf-core/tools/test_tuple_heading.rb +0 -15
- data/spec/unit/alf-core/tools/test_varargs.rb +0 -19
- data/spec/unit/alf-core/types/test_attr_list.rb +0 -188
- data/spec/unit/alf-core/types/test_attr_name.rb +0 -78
- data/spec/unit/alf-core/types/test_boolean.rb +0 -42
- data/spec/unit/alf-core/types/test_class_methods.rb +0 -24
- data/spec/unit/alf-core/types/test_heading.rb +0 -146
- data/spec/unit/alf-core/types/test_ordering.rb +0 -185
- data/spec/unit/alf-core/types/test_renaming.rb +0 -72
- data/spec/unit/alf-core/types/test_size.rb +0 -74
- data/spec/unit/alf-core/types/test_summarization.rb +0 -95
- data/spec/unit/alf-core/types/test_tuple_computation.rb +0 -96
- data/spec/unit/alf-core/types/test_tuple_expression.rb +0 -122
- data/spec/unit/alf-core/types/test_tuple_predicate.rb +0 -108
- data/spec/unit/alf-csv/input.csv +0 -3
- data/spec/unit/alf-csv/test_reader.rb +0 -66
- data/spec/unit/alf-csv/test_renderer.rb +0 -73
- data/spec/unit/alf-engine/compact/test_set.rb +0 -33
- data/spec/unit/alf-engine/compact/test_uniq.rb +0 -33
- data/spec/unit/alf-engine/group/test_hash.rb +0 -34
- data/spec/unit/alf-engine/join/test_hash.rb +0 -55
- data/spec/unit/alf-engine/materialize/test_array.rb +0 -28
- data/spec/unit/alf-engine/materialize/test_hash.rb +0 -76
- data/spec/unit/alf-engine/quota/test_cesure.rb +0 -34
- data/spec/unit/alf-engine/rank/test_cesure.rb +0 -47
- data/spec/unit/alf-engine/semi/test_hash.rb +0 -58
- data/spec/unit/alf-engine/sort/test_in_memory.rb +0 -32
- data/spec/unit/alf-engine/summarize/test_cesure.rb +0 -36
- data/spec/unit/alf-engine/summarize/test_hash.rb +0 -36
- data/spec/unit/alf-engine/test_aggregate.rb +0 -26
- data/spec/unit/alf-engine/test_autonum.rb +0 -24
- data/spec/unit/alf-engine/test_clip.rb +0 -34
- data/spec/unit/alf-engine/test_coerce.rb +0 -35
- data/spec/unit/alf-engine/test_compact.rb +0 -33
- data/spec/unit/alf-engine/test_concat.rb +0 -38
- data/spec/unit/alf-engine/test_defaults.rb +0 -37
- data/spec/unit/alf-engine/test_filter.rb +0 -23
- data/spec/unit/alf-engine/test_generator.rb +0 -25
- data/spec/unit/alf-engine/test_rename.rb +0 -24
- data/spec/unit/alf-engine/test_set_attr.rb +0 -38
- data/spec/unit/alf-engine/test_sort.rb +0 -32
- data/spec/unit/alf-engine/test_ungroup.rb +0 -28
- data/spec/unit/alf-engine/test_unwrap.rb +0 -20
- data/spec/unit/alf-engine/test_wrap.rb +0 -26
- data/spec/unit/alf-logs/apache_combined.log +0 -5
- data/spec/unit/alf-logs/postgresql.log +0 -29
- data/spec/unit/alf-logs/test_reader.rb +0 -57
- data/spec/unit/alf-sequel/alf.db +0 -0
- data/spec/unit/alf-sequel/test_environment.rb +0 -74
- data/spec/unit/alf-shell/doc_manager/dynamic.md +0 -1
- data/spec/unit/alf-shell/doc_manager/example.md +0 -1
- data/spec/unit/alf-shell/doc_manager/example_1.txt +0 -11
- data/spec/unit/alf-shell/doc_manager/static.md +0 -1
- data/spec/unit/alf-shell/doc_manager/test_call.rb +0 -48
- data/spec/unit/alf-shell/main/test_class_methods.rb +0 -44
- data/spec/unit/alf-shell/operator/test_autonum.rb +0 -28
- data/spec/unit/alf-shell/operator/test_clip.rb +0 -29
- data/spec/unit/alf-shell/operator/test_coerce.rb +0 -21
- data/spec/unit/alf-shell/operator/test_compact.rb +0 -16
- data/spec/unit/alf-shell/operator/test_defaults.rb +0 -29
- data/spec/unit/alf-shell/operator/test_extend.rb +0 -21
- data/spec/unit/alf-shell/operator/test_generator.rb +0 -37
- data/spec/unit/alf-shell/operator/test_group.rb +0 -32
- data/spec/unit/alf-shell/operator/test_heading.rb +0 -16
- data/spec/unit/alf-shell/operator/test_intersect.rb +0 -18
- data/spec/unit/alf-shell/operator/test_join.rb +0 -18
- data/spec/unit/alf-shell/operator/test_matching.rb +0 -18
- data/spec/unit/alf-shell/operator/test_minus.rb +0 -18
- data/spec/unit/alf-shell/operator/test_not_matching.rb +0 -18
- data/spec/unit/alf-shell/operator/test_project.rb +0 -38
- data/spec/unit/alf-shell/operator/test_quota.rb +0 -23
- data/spec/unit/alf-shell/operator/test_rank.rb +0 -30
- data/spec/unit/alf-shell/operator/test_rename.rb +0 -21
- data/spec/unit/alf-shell/operator/test_restrict.rb +0 -35
- data/spec/unit/alf-shell/operator/test_sort.rb +0 -49
- data/spec/unit/alf-shell/operator/test_summarize.rb +0 -30
- data/spec/unit/alf-shell/operator/test_ungroup.rb +0 -28
- data/spec/unit/alf-shell/operator/test_union.rb +0 -18
- data/spec/unit/alf-shell/operator/test_unwrap.rb +0 -28
- data/spec/unit/alf-shell/operator/test_wrap.rb +0 -30
- data/tasks/clean.rake +0 -3
- data/tasks/debug_mail.rake +0 -75
- data/tasks/debug_mail.txt +0 -18
- data/tasks/gh-pages.rake +0 -68
- data/tasks/integration_test.rake +0 -43
- data/tasks/regression_test.rake +0 -44
- data/tasks/unit_test.rake +0 -44
- data/tasks/yard.rake +0 -51
data/Gemfile.lock
CHANGED
@@ -1,45 +1,46 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
alf-core (0.13.0)
|
5
|
+
domain (~> 1.0)
|
6
|
+
myrrha (~> 3.0)
|
7
|
+
path (~> 1.3)
|
8
|
+
sexpr (~> 0.5.1)
|
9
|
+
alf-sequel (0.13.0)
|
10
|
+
alf-core (~> 0.13.0)
|
11
|
+
sequel (~> 3.48)
|
12
|
+
alf-shell (0.13.1)
|
13
|
+
alf-core (~> 0.13.0)
|
14
|
+
quickl (~> 0.4.3)
|
15
|
+
diff-lcs (1.2.4)
|
16
|
+
domain (1.0.0)
|
17
|
+
myrrha (3.0.0)
|
18
|
+
domain (~> 1.0)
|
19
|
+
path (1.3.1)
|
20
|
+
pg (0.14.1)
|
10
21
|
quickl (0.4.3)
|
11
|
-
rake (
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
rspec-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
rspec-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
sqlite3 (1.3.5)
|
24
|
-
wlang (0.10.2)
|
25
|
-
yard (0.7.5)
|
22
|
+
rake (10.1.0)
|
23
|
+
rspec (2.14.1)
|
24
|
+
rspec-core (~> 2.14.0)
|
25
|
+
rspec-expectations (~> 2.14.0)
|
26
|
+
rspec-mocks (~> 2.14.0)
|
27
|
+
rspec-core (2.14.4)
|
28
|
+
rspec-expectations (2.14.0)
|
29
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
30
|
+
rspec-mocks (2.14.1)
|
31
|
+
sequel (3.48.0)
|
32
|
+
sexpr (0.5.1)
|
33
|
+
sqlite3 (1.3.7)
|
26
34
|
|
27
35
|
PLATFORMS
|
28
36
|
ruby
|
29
37
|
|
30
38
|
DEPENDENCIES
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
highline (~> 1.6.2)
|
39
|
+
alf-core (~> 0.13.0)
|
40
|
+
alf-sequel (~> 0.13.0)
|
41
|
+
alf-shell (~> 0.13.1)
|
35
42
|
jdbc-sqlite3 (~> 3.7)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
redcarpet (~> 2.1.0)
|
40
|
-
request-log-analyzer (~> 1.11.0)
|
41
|
-
rspec (~> 2.8.0)
|
42
|
-
sequel (~> 3.30)
|
43
|
+
pg (~> 0.14)
|
44
|
+
rake (~> 10.1)
|
45
|
+
rspec (~> 2.14)
|
43
46
|
sqlite3 (~> 1.3)
|
44
|
-
wlang (~> 0.10.2)
|
45
|
-
yard (~> 0.7.2)
|
data/LICENCE.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# The MIT Licence
|
2
2
|
|
3
|
-
Copyright (c)
|
3
|
+
Copyright (c) 2013 - Bernard Lambeau
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining
|
6
6
|
a copy of this software and associated documentation files (the
|
data/Manifest.txt
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
bin/**/*
|
2
|
-
doc/commands/**/*
|
3
|
-
doc/operators/**/*
|
4
|
-
examples/**/*
|
5
|
-
lib/**/*
|
6
|
-
spec/**/*
|
7
|
-
tasks/**/*
|
8
|
-
Rakefile
|
9
1
|
alf.gemspec
|
10
2
|
alf.noespec
|
3
|
+
.gemtest
|
11
4
|
CHANGELOG.md
|
12
5
|
Gemfile
|
13
6
|
Gemfile.lock
|
7
|
+
bin/**/*
|
8
|
+
lib/**/*
|
14
9
|
LICENCE.md
|
15
10
|
Manifest.txt
|
11
|
+
Rakefile
|
16
12
|
README.md
|
17
|
-
|
13
|
+
spec/**/*
|
14
|
+
tasks/**/*
|
15
|
+
test/**/*
|
data/README.md
CHANGED
@@ -1,567 +1,27 @@
|
|
1
|
-
# Alf
|
1
|
+
# Alf
|
2
2
|
|
3
|
-
|
4
|
-
[![Dependency Status](https://gemnasium.com/blambeau/alf.png)](https://gemnasium.com/blambeau/alf)
|
3
|
+
Relational Algebra at your fingertips
|
5
4
|
|
6
|
-
|
5
|
+
[![Build Status](https://secure.travis-ci.org/alf-tool/alf.png)](http://travis-ci.org/alf-tool/alf)
|
6
|
+
[![Dependency Status](https://gemnasium.com/alf-tool/alf.png)](https://gemnasium.com/alf-tool/alf)
|
7
7
|
|
8
|
-
|
8
|
+
## Links
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
provide data structures like arrays, hashes, sets, trees and graphs but not
|
14
|
-
_relations_... Let's stop the segregation ;-)
|
10
|
+
* [Official documentation](http://blambeau.github.com/alf)
|
11
|
+
* [Source and Issues](http://github.com/alf-tool/alf)
|
12
|
+
* [Relational basics as needed](http://www.revision-zero.org/relational-basics-2)
|
15
13
|
|
16
|
-
|
14
|
+
## What & Why
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# API is not considered stable enough for now, please use
|
24
|
-
gem "alf", "= 0.10.0"
|
25
|
-
|
26
|
-
# The following should not break your code, but is a bit less safe,
|
27
|
-
# until 1.0.0 has been reached
|
28
|
-
gem "alf", "~> 0.10.0"
|
29
|
-
|
30
|
-
### Links
|
31
|
-
|
32
|
-
* http://blambeau.github.com/alf
|
33
|
-
* http://rubydoc.info/gems/alf
|
34
|
-
* http://github.com/blambeau/alf
|
35
|
-
* http://rubygems.org/gems/alf
|
36
|
-
|
37
|
-
### Quick overview
|
38
|
-
|
39
|
-
Alf is a commandline tool and Ruby library to manipulate data with all the power
|
40
|
-
of a truly relational algebra approach. Objectives behind Alf are manifold:
|
41
|
-
|
42
|
-
* Pragmatically, Alf aims at being a useful commandline executable for manipulating
|
43
|
-
relational-like data: database records, csv files, or **whatever can be interpreted
|
44
|
-
as (the physical encoding of) a relation**. See 'alf --help' for the list of
|
45
|
-
available commands and implemented relational operators.
|
46
|
-
|
47
|
-
% alf restrict suppliers -- "city == 'London'" | alf join cities
|
48
|
-
|
49
|
-
* Alf is also a 100% Ruby relational algebra implementation shipped with a simple
|
50
|
-
to use, powerful, functional DSL for compiling and evaluating relational queries.
|
51
|
-
Alf is not limited to simple scalar values, but admits values of arbitrary
|
52
|
-
complexity (under a few requirements about their implementation, see next
|
53
|
-
section). See 'alf --help' as well as .alf files in the examples directory
|
54
|
-
for syntactic examples.
|
55
|
-
|
56
|
-
Alf.lispy.evaluate {
|
57
|
-
(join (restrict :suppliers, lambda{ city == 'London' }), :cities)
|
58
|
-
}
|
59
|
-
|
60
|
-
In addition to this functional syntax, Alf comes bundled with an in-memory
|
61
|
-
Relation data structure that provides an object-oriented way of manipulating
|
62
|
-
relations in simplest cases:
|
63
|
-
|
64
|
-
suppliers = Alf::Relation[
|
65
|
-
{:sid => 'S1', :name => 'Smith', :status => 20, :city => 'London'},
|
66
|
-
{:sid => 'S2', :name => 'Jones', :status => 10, :city => 'Paris'},
|
67
|
-
{:sid => 'S3', :name => 'Blake', :status => 30, :city => 'Paris'},
|
68
|
-
{:sid => 'S4', :name => 'Clark', :status => 20, :city => 'London'},
|
69
|
-
{:sid => 'S5', :name => 'Adams', :status => 30, :city => 'Athens'},
|
70
|
-
]
|
71
|
-
cities = ...
|
72
|
-
puts suppliers.restrict(lambda{ city == 'London' }).join(cities)
|
73
|
-
|
74
|
-
* Alf is also an educational tool, that I've written to draw people attention
|
75
|
-
about the ill-known relational theory (and ill-represented by SQL). The tool
|
76
|
-
is largely inspired from **Tutorial D**, the tutorial language of Chris Date and
|
77
|
-
Hugh Darwen in their books, more specifically in
|
78
|
-
{http://www.thethirdmanifesto.com/ *The Third Manifesto* (TTM)}.
|
79
|
-
However, Alf only provides an overview of the relational _algebra_ defined
|
80
|
-
there (Alf is neither a relational _database_, nor a relational _language_).
|
81
|
-
I hope that people (especially talented developers) will be sufficiently
|
82
|
-
enticed by features shown here to open that book, read it more deeply, and
|
83
|
-
implement new stuff around Date & Darwen's vision. Have a look at the result of
|
84
|
-
the following query for the kind of things that you'll never ever have in SQL
|
85
|
-
(see also 'alf help quota', 'alf help wrap', 'alf help group', ...):
|
86
|
-
|
87
|
-
% alf --text summarize supplies -- sid -- total "sum{ qty }" which "collect{ pid }"
|
88
|
-
|
89
|
-
* Last, but not least, Alf is an attempt to help me test some research ideas and
|
90
|
-
communicate about them with people that already know (all or part) of the TTM
|
91
|
-
vision of relational theory. These people include members of the TTM mailing
|
92
|
-
list as well as other people implementing some of the TTM ideas (see
|
93
|
-
{https://github.com/dkubb/veritas Dan Kubb's Veritas project} for example). For
|
94
|
-
this reason, specific features and/or operators are mine, should be considered
|
95
|
-
'research work in progress', and used with care because not necessarily in
|
96
|
-
conformance with the TTM.
|
97
|
-
|
98
|
-
% alf --text quota supplies -- sid -- qty -- pos "count()"
|
99
|
-
|
100
|
-
## Overview of relational theory
|
101
|
-
|
102
|
-
We quickly recall relational theory in this section, as described in the TTM
|
103
|
-
book. Readers not familiar with Date and Darwen's vision of relational theory
|
104
|
-
should probably read this section, even if fluent in SQL. Others may probably
|
105
|
-
skip this section. A quick test?
|
106
|
-
|
107
|
-
> _A relation is a value, precisely a set of tuples, which are themselves values.
|
108
|
-
Therefore, a relation is immutable, not ordered, does not contain duplicates,
|
109
|
-
and does not have null/nil attributes._
|
110
|
-
|
111
|
-
Familiar? Skip. Otherwise, read on.
|
112
|
-
|
113
|
-
### The example database
|
114
|
-
|
115
|
-
This README file shows a lot of examples built on top of the following suppliers
|
116
|
-
& parts database (almost identical to the original version in C. J. Date's database
|
117
|
-
books). By default, the alf command line is wired to this embedded example. All
|
118
|
-
examples shown here should therefore work immediately, if you want to reproduce
|
119
|
-
them!
|
120
|
-
|
121
|
-
% alf show database
|
122
|
-
|
123
|
-
+-------------------------------------+-------------------------------------------------+-------------------------+------------------------+
|
124
|
-
| :suppliers | :parts | :cities | :supplies |
|
125
|
-
+-------------------------------------+-------------------------------------------------+-------------------------+------------------------+
|
126
|
-
| +------+-------+---------+--------+ | +------+-------+--------+------------+--------+ | +----------+----------+ | +------+------+------+ |
|
127
|
-
| | :sid | :name | :status | :city | | | :pid | :name | :color | :weight | :city | | | :city | :country | | | :sid | :pid | :qty | |
|
128
|
-
| +------+-------+---------+--------+ | +------+-------+--------+------------+--------+ | +----------+----------+ | +------+------+------+ |
|
129
|
-
| | S1 | Smith | 20 | London | | | P1 | Nut | Red | 12.0000000 | London | | | London | England | | | S1 | P1 | 300 | |
|
130
|
-
| | S2 | Jones | 10 | Paris | | | P2 | Bolt | Green | 17.0000000 | Paris | | | Paris | France | | | S1 | P2 | 200 | |
|
131
|
-
| | S3 | Blake | 30 | Paris | | | P3 | Screw | Blue | 17.0000000 | Oslo | | | Athens | Greece | | | S1 | P3 | 400 | |
|
132
|
-
| | S4 | Clark | 20 | London | | | P4 | Screw | Red | 14.0000000 | London | | | Brussels | Belgium | | | S1 | P4 | 200 | |
|
133
|
-
| | S5 | Adams | 30 | Athens | | | P5 | Cam | Blue | 12.0000000 | Paris | | +----------+----------+ | | S1 | P5 | 100 | |
|
134
|
-
| +------+-------+---------+--------+ | | P6 | Cog | Red | 19.0000000 | London | | | | S1 | P6 | 100 | |
|
135
|
-
| | +------+-------+--------+------------+--------+ | | | S2 | P1 | 300 | |
|
136
|
-
| | | | | S2 | P2 | 400 | |
|
137
|
-
| | | | | S3 | P2 | 200 | |
|
138
|
-
| | | | | S4 | P2 | 200 | |
|
139
|
-
| | | | | S4 | P4 | 300 | |
|
140
|
-
| | | | | S4 | P5 | 400 | |
|
141
|
-
| | | | +------+------+------+ |
|
142
|
-
+-------------------------------------+-------------------------------------------------+-------------------------+------------------------+
|
143
|
-
|
144
|
-
Many people think that relational databases are necessary 'flat', that they are
|
145
|
-
necessarily limited to simple scalar values put in two dimension tables. This is
|
146
|
-
wrong; most SQL databases are indeed 'flat', but _relations_ (in the mathematical
|
147
|
-
sense of the relational theory) are not! Look, **the example above is a relation!**;
|
148
|
-
that 'contains' other relations as particular values, which, in turn, could
|
149
|
-
'contain' relations or any other 'simple' or more 'complex' value... This is not
|
150
|
-
"flat" at all, after all :-)
|
151
|
-
|
152
|
-
### Types and Values
|
153
|
-
|
154
|
-
To understand what is a relation exactly, one needs to remember elementary
|
155
|
-
notions of set theory and the concepts of _type_ and _value_.
|
156
|
-
|
157
|
-
* A _type_ is a finite set of values; it is not particularly ordered and, being
|
158
|
-
a set, it does never contain two values which are equal (any type is necessarily
|
159
|
-
accompanied with an equality operator, denoted here by '==').
|
160
|
-
|
161
|
-
* A _value_ is **immutable** (you cannot 'change' a value, in any way), has no
|
162
|
-
localization in time and space, and is always typed (that is, it is always
|
163
|
-
accompanied by some identification of the type it belongs to).
|
164
|
-
|
165
|
-
As you can see, _type_ and _value_ are not the same concepts as _class_ and
|
166
|
-
_object_, which you are probably more familiar with. Alf considers that the
|
167
|
-
latter are _implementations_ of the former. Alf assumes _valid_ implementations
|
168
|
-
(equality and hash methods must be correct) and _valid_ usage (objects used for
|
169
|
-
representing values are kept immutable in practice). Alf _assumes_ this, but
|
170
|
-
does not _enforces_ it: it is your responsibility to use Alf in conformance with
|
171
|
-
these preconditions. That being said, if you want **arrays, colors, ranges, or
|
172
|
-
whatever in your relations**, just do it! You can even join on them, restrict on
|
173
|
-
them, summarize on them, and so on:
|
174
|
-
|
175
|
-
% alf extend suppliers -- chars "name.chars.to_a" | alf --text restrict -- "chars.last == 's'"
|
176
|
-
|
177
|
-
+------+-------+---------+--------+-----------------+
|
178
|
-
| :sid | :name | :status | :city | :chars |
|
179
|
-
+------+-------+---------+--------+-----------------+
|
180
|
-
| S2 | Jones | 10 | Paris | [J, o, n, e, s] |
|
181
|
-
| S5 | Adams | 30 | Athens | [A, d, a, m, s] |
|
182
|
-
+------+-------+---------+--------+-----------------+
|
183
|
-
|
184
|
-
A last, very important word about values. **Null/nil is not a value**. Strictly
|
185
|
-
speaking therefore, you may not use null/nil inside your data files or datasources
|
186
|
-
representing relations. That being said, Alf provides specific support for handling
|
187
|
-
them, because they appear in today's databases in practice and that Alf aims at
|
188
|
-
being a tool that helps you tackling _practical_ problems. See the section with
|
189
|
-
title "Why is Alf Exactly?" later.
|
190
|
-
|
191
|
-
### Tuples and Relations
|
192
|
-
|
193
|
-
Tuples (aka records) and relations are values as well, which explains why you
|
194
|
-
can have them inside relations!
|
195
|
-
|
196
|
-
* Logically speaking, a tuple is a set of (attribute name, attribute value)
|
197
|
-
pairs. Moreover, it does not contain two attributes with the same name and is
|
198
|
-
**not particularly ordered**. Also, **a tuple is a _value_, and is therefore
|
199
|
-
immutable**. Last, but not least, a tuple **does not admit nulls/nils**. Tuples
|
200
|
-
in Alf are simply implemented with ruby hashes, taken as tuple implementations.
|
201
|
-
Not all hashes are valid tuple implementations, of course (those containing nil
|
202
|
-
are not, for example). Alf _assumes_ valid tuples, but does not _enforce_ this
|
203
|
-
precondition. It's up to you to use Alf the right way! No support is or will
|
204
|
-
ever be provided for ordering tuple attributes. However, as hashes are ordered
|
205
|
-
in Ruby 1.9, Alf implements a best effort strategy to keep a friendly ordering
|
206
|
-
when rendering tuples and relations. This is a very good practical reason for
|
207
|
-
migrating to ruby 1.9 if not already done!
|
208
|
-
|
209
|
-
{:sid => "S1", :name => "Smith", :status => 20, :city => "London"}
|
210
|
-
|
211
|
-
* A _relation_ is a set of tuples. Being a set, a relation does **never contain
|
212
|
-
duplicates** (unlike SQL that works on bags, not on sets) and is **not
|
213
|
-
particularly ordered**. Moreover, all tuples of a relation must have the same
|
214
|
-
_heading_, that is, the same set of attribute (name, type) pairs. Also, **a
|
215
|
-
relation is a _value_, is therefore immutable** and **does not admit null/nil**.
|
216
|
-
|
217
|
-
Alf is mainly an implementation of relational algebra (see section below). The
|
218
|
-
implemented operators consider any Iterator of tuples as potentially valid
|
219
|
-
operand. In addition Alf provides a Relation ruby class, that acts as an
|
220
|
-
in-memory data structure that provides an Object-Oriented API to call operators
|
221
|
-
(see "Interfacing Alf in Ruby" below).
|
222
|
-
|
223
|
-
### Relational Algebra
|
224
|
-
|
225
|
-
In classical algebra, you can make computations like <code>(5 + 2) - 3</code>.
|
226
|
-
In relational algebra, you can make similar things on relations. Alf uses an
|
227
|
-
infix, functional programming-oriented syntax for algebra expressions:
|
228
|
-
|
229
|
-
(minus (union :suppliers, xxx), yyy)
|
230
|
-
|
231
|
-
All relational operators take relation operands in input and return a relation
|
232
|
-
as output. We say that the relational algebra is _closed_ under its operators.
|
233
|
-
In practice, it means that operands may always be sub-expressions, **always**.
|
234
|
-
|
235
|
-
(minus (union (restrict :suppliers, lambda{ zzz }), xxx), yyy)
|
236
|
-
|
237
|
-
In shell, the closure property means that you can pipe alf invocations the way
|
238
|
-
you want! The same query, in shell:
|
239
|
-
|
240
|
-
alf restrict suppliers -- "zzz" | alf union xxx | alf minus yyy
|
241
|
-
|
242
|
-
## What is Alf exactly?
|
243
|
-
|
244
|
-
*The Third Manifesto* defines a series of prescriptions, proscriptions and very
|
245
|
-
strong suggestions for designing a truly relational _language_, called a _D_,
|
246
|
-
as an alternative to SQL for managing relational databases. This is far behind
|
247
|
-
my objective with Alf, as it does not touch at database issues at all (persistence,
|
248
|
-
transactions, and so on.) and don't actually define a programming language (only
|
249
|
-
a small functional ruby DSL).
|
250
|
-
|
251
|
-
Alf must simply be interpreted as a ruby library implementing (a variant of)
|
252
|
-
Date and Darwen's relational algebra. This library is designed as a set of operator
|
253
|
-
implementations, that work as tuple iterators taking other tuple iterators as
|
254
|
-
input. Under the pre-condition that you provide them _valid_ tuple iterators as
|
255
|
-
input (no duplicates, no nil, + other preconditions on an operator basis), the
|
256
|
-
result is a valid iterator as well. Unless explicitely stated otherwise, any
|
257
|
-
behavior observed when not respecting these preconditions, even an interesting
|
258
|
-
behavior, is not guaranteed and might change with tiny version changes (see
|
259
|
-
section about versioning policy at the end of this file).
|
260
|
-
|
261
|
-
### The command line utility
|
262
|
-
|
263
|
-
#
|
264
|
-
# Provided that suppliers and cities are valid relation representations
|
265
|
-
# [something similar]
|
266
|
-
#
|
267
|
-
% alf restrict suppliers -- "city == 'London'" | alf join cities
|
268
|
-
|
269
|
-
# the resulting stream is a valid relation representation in the output
|
270
|
-
# stream format that you have selected (.rash by default). It can therefore
|
271
|
-
# be piped to another alf shell invocation, or saved to a file and re-read
|
272
|
-
# later (under the assumption that input and output data formats match, or
|
273
|
-
# course). [Something similar about responsibility and bug].
|
274
|
-
|
275
|
-
If you take a look at .alf example files, you'll find functional ruby expressions
|
276
|
-
like the following (called Lispy expressions):
|
277
|
-
|
278
|
-
% cat examples/operators/minus.alf
|
279
|
-
|
280
|
-
# Give all suppliers, except those living in Paris
|
281
|
-
(minus :suppliers,
|
282
|
-
(restrict :suppliers, lambda{ city == 'Paris' }))
|
283
|
-
|
284
|
-
# This is a contrived example for illustrating minus, as the
|
285
|
-
# following is equivalent
|
286
|
-
(restrict :suppliers, lambda{ city != 'Paris' })
|
287
|
-
|
288
|
-
You can simply execute such expressions with the alf command line itself (the
|
289
|
-
three following invocations return the same result):
|
290
|
-
|
291
|
-
% alf examples/operators/minus.alf | alf show
|
292
|
-
% alf show minus
|
293
|
-
% alf -e "(restrict :suppliers, lambda{ city != 'Paris' })" | alf show
|
294
|
-
|
295
|
-
Symbols are magically resolved from the environment, which is wired to the
|
296
|
-
examples by default. See the dedicated sections below to update this behavior
|
297
|
-
to your needs.
|
298
|
-
|
299
|
-
### The algebra compiler
|
300
|
-
|
301
|
-
#
|
302
|
-
# Provided that :suppliers and :cities are valid relation representations
|
303
|
-
# (under the responsibility shared by you and the Reader and Environment
|
304
|
-
# subclasses you use -- see later), then,
|
305
|
-
#
|
306
|
-
op = Alf.lispy.compile {
|
307
|
-
(join (restrict :suppliers, lambda{ city == 'London' }), :cities)
|
308
|
-
}
|
309
|
-
|
310
|
-
# op is a thread-safe Enumerable of tuples, that can be taken as a valid
|
311
|
-
# relation representation. It can therefore be used as the input operand
|
312
|
-
# of any other expression. This is under Alf's responsibility, and any
|
313
|
-
# failure must be considered a bug!
|
314
|
-
|
315
|
-
### The Relation data structure
|
316
|
-
|
317
|
-
In addition, Alf is bundled with an in-memory Relation data structure that
|
318
|
-
provided a more abstract API for manipulating relations in simple cases (the
|
319
|
-
rules are the same about pre and post-conditions):
|
320
|
-
|
321
|
-
# The query above can be done as follows. Note that relations are always
|
322
|
-
# loaded in memory here!
|
323
|
-
suppliers = Alf::Relation[ ... ]
|
324
|
-
cities = Alf::Relation[ ... ]
|
325
|
-
suppliers.restrict(lambda{ city == 'London' }).
|
326
|
-
join(cities)
|
327
|
-
# => Alf::Relation[ ... ]
|
328
|
-
|
329
|
-
All relational operators have an instance method equivalent on the Alf::Relation
|
330
|
-
class. Semantically, the receiver object is simply the first operand of the
|
331
|
-
functional call, as illustrated above.
|
332
|
-
|
333
|
-
### Where do relations come from?
|
334
|
-
|
335
|
-
Relation literals can simply be written as follows:
|
336
|
-
|
337
|
-
suppliers = Alf::Relation[
|
338
|
-
{:sid => 'S1', :name => 'Smith', :status => 20, :city => 'London'},
|
339
|
-
{:sid => 'S2', :name => 'Jones', :status => 10, :city => 'Paris'},
|
340
|
-
{:sid => 'S3', :name => 'Blake', :status => 30, :city => 'Paris'},
|
341
|
-
{:sid => 'S4', :name => 'Clark', :status => 20, :city => 'London'},
|
342
|
-
{:sid => 'S5', :name => 'Adams', :status => 30, :city => 'Athens'},
|
343
|
-
]
|
344
|
-
|
345
|
-
Environment classes serve datasets (see later) that always have a to_rel method
|
346
|
-
for obtaining in-memory relations:
|
347
|
-
|
348
|
-
env = Alf::Environment.examples
|
349
|
-
env.dataset(:suppliers).to_rel
|
350
|
-
# => Alf::Relation[ ... ]
|
351
|
-
|
352
|
-
Compiled expressions always have a to_rel method that allows obtaining an
|
353
|
-
in-memory relation:
|
354
|
-
|
355
|
-
op = Alf.lispy.compile {
|
356
|
-
(join (restrict :suppliers, lambda{ city == 'London' }), :cities)
|
357
|
-
}
|
358
|
-
op.to_rel
|
359
|
-
# => Alf::Relation[...]
|
360
|
-
|
361
|
-
Lispy provides an 'evaluate' method which is precisely equivalent to the chain
|
362
|
-
above. Therefore:
|
363
|
-
|
364
|
-
rel = Alf.lispy.evaluate {
|
365
|
-
(join (restrict :suppliers, lambda{ city == 'London' }), :cities)
|
366
|
-
}
|
367
|
-
# => Alf::Relation[...]
|
368
|
-
|
369
|
-
### Algebra is closed under its operators!
|
370
|
-
|
371
|
-
Of course, from the closure property of a relational algebra (that states that
|
372
|
-
operators works on relations and return relations), you can use a sub expression
|
373
|
-
*everytime* a relational operand is expected, everytime:
|
374
|
-
|
375
|
-
# Compute the total qty supplied in each country together with the subset
|
376
|
-
# of products shipped there. Only consider suppliers that have a status
|
377
|
-
# greater than 10, however.
|
378
|
-
(summarize \
|
379
|
-
(join \
|
380
|
-
(join (restrict :suppliers, lambda{ status > 10 }),
|
381
|
-
:supplies),
|
382
|
-
:cities),
|
383
|
-
[:country],
|
384
|
-
:which => Agg::collect(:pid),
|
385
|
-
:total => Agg::sum{ qty })
|
386
|
-
|
387
|
-
Of course, complex queries quickly become unreadable that way. But you can always
|
388
|
-
split complex tasks in more simple ones:
|
389
|
-
|
390
|
-
kept_suppliers = (restrict :suppliers, lambda{ status > 10 })
|
391
|
-
with_countries = (join kept_suppliers, :cities),
|
392
|
-
supplying = (join with_countries, :supplies)
|
393
|
-
(summarize supplying,
|
394
|
-
[:country],
|
395
|
-
:which => Agg::collect(:pid),
|
396
|
-
:total => Agg::sum{ qty })
|
397
|
-
|
398
|
-
And here is the result !
|
399
|
-
|
400
|
-
+------+--------+--------------------------+
|
401
|
-
| :sid | :total | :which |
|
402
|
-
+------+--------+--------------------------+
|
403
|
-
| S1 | 1300 | [P1, P2, P3, P4, P5, P6] |
|
404
|
-
| S2 | 700 | [P1, P2] |
|
405
|
-
| S3 | 200 | [P2] |
|
406
|
-
| S4 | 900 | [P2, P4, P5] |
|
407
|
-
+------+--------+--------------------------+
|
408
|
-
|
409
|
-
### Reference API
|
410
|
-
|
411
|
-
For now, the Ruby API is documented in the commandline help itself (a cheatsheet
|
412
|
-
or something will be provided as soon as possible). For example, you'll find the
|
413
|
-
allowed syntaxes for RESTRICT as follows:
|
414
|
-
|
415
|
-
% alf help restrict
|
416
|
-
|
417
|
-
...
|
418
|
-
API & EXAMPLE
|
419
|
-
|
420
|
-
# Restrict to suppliers with status greater than 20
|
421
|
-
(restrict :suppliers, lambda{ status > 20 })
|
422
|
-
|
423
|
-
# Restrict to suppliers that live in London
|
424
|
-
(restrict :suppliers, lambda{ city == 'London' })
|
425
|
-
...
|
426
|
-
|
427
|
-
### Coping with non-relational data sources (nil, duplicates, etc.)
|
428
|
-
|
429
|
-
Alf aims at being a tool that helps you tackling practical problems, and
|
430
|
-
denormalized and/or noisy data is one of them. Missing values occur. Duplicates
|
431
|
-
abound in SQL databases lacking primary keys, and so on. Using Alf's relational
|
432
|
-
operators on such inputs is not a good idea, because it is a strong precondition
|
433
|
-
violation. This is not because relational theory is weak, but because extending
|
434
|
-
it to handle null/nil and duplicates correctly has been proven at best a nightmare,
|
435
|
-
and at worse a mess. As a practical exercice, try to extend classical algebra
|
436
|
-
with versions of +, - * and / that handle nil in such a way that the resulting
|
437
|
-
theory is sound and still looks intuitive! Then do it on boolean algebra with
|
438
|
-
_and_, _or_ and _not_. Then, add null/nil to classical set theory. Classical
|
439
|
-
algebra, boolean algebra, and set theory are important building blocks behind
|
440
|
-
relational algebra because almost all of its operators are defined on top of
|
441
|
-
them...
|
442
|
-
|
443
|
-
So what? The approach choosen in Alf to handle this conflict is very pragmatic.
|
444
|
-
First of all, Alf implements a best effort strategy -- where possible -- to
|
445
|
-
remain friendly in presence of null/nil on attributes that have no influence on
|
446
|
-
an operator's job. For example, the query below will certainly fail if _status_
|
447
|
-
is null/nil, but it won't probably fail if any other attribute is nil.
|
448
|
-
|
449
|
-
% alf restrict suppliers -- "status > 10"
|
450
|
-
|
451
|
-
This best-effort strategy is not enough, and striclty speaking, must be considered
|
452
|
-
unsound (for example, it strongly hurts optimization possibilities). Therefore,
|
453
|
-
I strongly encourage you to go a step further: **if relational operators want
|
454
|
-
true relations as input, please, give them!**. For this, Alf also provides a few
|
455
|
-
non-relational operators in addition to relational ones. Those operators must be
|
456
|
-
interpreted as "pre-relational" operators, in the sense that they help obtaining
|
457
|
-
valid relation representations from invalid ones. Provided that you use them
|
458
|
-
correctly, their output can safely be used as input of a relational operator.
|
459
|
-
You'll find,
|
460
|
-
|
461
|
-
* <code>alf autonum</code> -- ensure no duplicates by generating a unique attribute
|
462
|
-
* <code>alf compact</code> -- brute-force duplicates removal
|
463
|
-
* <code>alf defaults</code> -- replace nulls/nil by valid values, on an attribute basis
|
464
|
-
|
465
|
-
Play the game, it's easy!
|
466
|
-
|
467
|
-
- _Give id, name and status of suppliers whose status is greater that 10_
|
468
|
-
- Hey man, we don't know the status for all suppliers! What about these cases?
|
469
|
-
- _Ignore them_
|
470
|
-
- No problem dude!
|
471
|
-
|
472
|
-
% alf defaults --strict suppliers -- sid '' name '' status 0 | alf restrict -- "status > 10"
|
473
|
-
|
474
|
-
### Alf is duck-typed
|
475
|
-
|
476
|
-
The relational theory is often considered under a statically-typed point
|
477
|
-
of view. When considering tuples and relations, for example, the notion of
|
478
|
-
_heading_, a set of (name,type) pairs, is central. For example, a heading for
|
479
|
-
a supplier tuple/relation could be:
|
480
|
-
|
481
|
-
{:sid => String, :name => Name, :status => Integer, :city => String}
|
482
|
-
|
483
|
-
Most relational operators have preconditions in terms of the headings of their
|
484
|
-
operands. For example, _minus_ and _union_ require their operands to have same
|
485
|
-
heading, while _rename_ requires renamed attributes to exist in operand's
|
486
|
-
heading, and so on. Given an expression in relational algebra, it is always
|
487
|
-
possible to compute the heading of the resulting relation, by statically
|
488
|
-
analyzing the whole query expression in the light of a catalog of typed
|
489
|
-
operators. This way, a tool can check that a query is statically valid, i.e.
|
490
|
-
that it respects operator preconditions. While this approach has the major
|
491
|
-
advantage of allowing strong optimizations, it also has a few drawbacks (as
|
492
|
-
the need to know the heading of used datasources in advance) and is difficult to
|
493
|
-
mary with dynamically-typed languages like Ruby. Therefore, Alf takes another
|
494
|
-
approach, which is similar to duck-typing. In essence, this approach can be
|
495
|
-
summarized as follows:
|
496
|
-
|
497
|
-
- _You have the responsibility of not violating operators' preconditions. If you
|
498
|
-
do, Alf has the responsibility of returning correct results._.
|
499
|
-
- No problem dude!
|
500
|
-
|
501
|
-
## More about the shell command line
|
502
|
-
|
503
|
-
% alf --help
|
504
|
-
|
505
|
-
The help command will display the list of available operators. Each of them is
|
506
|
-
completely described with 'alf help OPERATOR'. They all have a similar invocation
|
507
|
-
syntax in shell:
|
508
|
-
|
509
|
-
% alf operator operands... -- args...
|
510
|
-
|
511
|
-
For example, try the following:
|
512
|
-
|
513
|
-
# display suppliers that live in Paris
|
514
|
-
% alf restrict suppliers -- "city == 'Paris'"
|
515
|
-
|
516
|
-
# join suppliers and cities (no args here)
|
517
|
-
% alf join suppliers cities
|
518
|
-
|
519
|
-
### Recognized data streams/files (.rash files)
|
520
|
-
|
521
|
-
For educational purposes, 'suppliers' and 'cities' inputs are magically resolved
|
522
|
-
as denoting the files examples/operators/suppliers.rash and
|
523
|
-
examples/operators/cities.rash, respectively. You'll find other data files:
|
524
|
-
parts.rash, supplies.rash that are resolved magically as well and with which you
|
525
|
-
can play. For non-educational purposes, operands may always be explicit files,
|
526
|
-
or you can force the folder in which datasource files have to be found:
|
527
|
-
|
528
|
-
# The following invocations are equivalent
|
529
|
-
% alf restrict /tmp/foo.rash -- "..."
|
530
|
-
% alf --env=/tmp restrict foo -- "..."
|
531
|
-
|
532
|
-
A .rash file is simply a file in which each line is a ruby Hash, intended to
|
533
|
-
represent a tuple. Under theory-driven preconditions, a .rash file can be seen
|
534
|
-
as a valid (straightforward but useful) physical representation of a relation!
|
535
|
-
When used in shell, alf dumps query results in the .rash format by default,
|
536
|
-
which opens the ability of piping invocations! Indeed, unary operators read their
|
537
|
-
operand on standard input if not specific as command argument. For example, the
|
538
|
-
invocation below is equivalent to the one given above.
|
539
|
-
|
540
|
-
# display suppliers that live in Paris
|
541
|
-
% cat examples/operators/suppliers.rash | alf restrict -- "city == 'Paris'"
|
542
|
-
|
543
|
-
Similarly, when only one operand is present in invocations of binary operators,
|
544
|
-
they read their left operand from standard input. Therefore, the join given in
|
545
|
-
previous section can also be written as follows:
|
546
|
-
|
547
|
-
% cat examples/operators/suppliers.rash | alf join cities
|
548
|
-
|
549
|
-
The relational algebra is _closed_ under its operators, which means that these
|
550
|
-
operators take relations as operands and return a relation. Therefore operator
|
551
|
-
invocations can be nested, that is, operands can be other relational expressions.
|
552
|
-
When you use alf in a shell, it simply means that you can pipe operators as you
|
553
|
-
want:
|
16
|
+
Alf brings the relational algebra both in Shell and in Ruby. In Shell, because
|
17
|
+
manipulating any relation-like data source should be as straightforward as a one-liner.
|
18
|
+
In Ruby, because I've never understood why programming languages provide data structures
|
19
|
+
like arrays, hashes, sets, trees and graphs but not _relations_...
|
554
20
|
|
555
|
-
|
21
|
+
## Shell Example
|
556
22
|
|
557
|
-
|
23
|
+
% alf --examples show suppliers
|
558
24
|
|
559
|
-
The show command (which is **not** a relational operator) can be used to obtain
|
560
|
-
a more friendly output:
|
561
|
-
|
562
|
-
# it renders a text table by default
|
563
|
-
% alf show [--text] suppliers
|
564
|
-
|
565
25
|
+------+-------+---------+--------+
|
566
26
|
| :sid | :name | :status | :city |
|
567
27
|
+------+-------+---------+--------+
|
@@ -572,226 +32,112 @@ a more friendly output:
|
|
572
32
|
| S5 | Adams | 30 | Athens |
|
573
33
|
+------+-------+---------+--------+
|
574
34
|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
#
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
#
|
633
|
-
#
|
634
|
-
#
|
635
|
-
#
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
# Returned operator is an enumerable of ruby hashes. Provided that datasets
|
661
|
-
# offered by the environment (:suppliers here) can be enumerated more than
|
662
|
-
# once, the operator may be used multiple times and is even thread safe!
|
663
|
-
#
|
664
|
-
london_suppliers.each do |tuple|
|
665
|
-
# tuple is a ruby Hash
|
666
|
-
end
|
667
|
-
|
668
|
-
#
|
669
|
-
# Now, maybe you want to reuse op in a larger query, for example
|
670
|
-
# by projecting on the city attribute... Here is how this can be
|
671
|
-
# done:
|
672
|
-
#
|
673
|
-
projection = (project london_suppliers, [:city])
|
674
|
-
|
675
|
-
Note that the examples above manipulate algebra operators, not relations per se.
|
676
|
-
This means that equality and other such operators, that operate on relation
|
677
|
-
_values_, do not operate correctly here:
|
678
|
-
|
679
|
-
projection == Alf::Relation[{:city => 'London'}]
|
680
|
-
# => nil
|
681
|
-
|
682
|
-
In contrast, you can use such operators when operating on true relation values:
|
683
|
-
|
684
|
-
projection.to_rel == Alf::Relation[{:city => 'London'}]
|
685
|
-
# => true
|
686
|
-
|
687
|
-
### Using/Implementing other Environments
|
688
|
-
|
689
|
-
An Environment instance if passed as first argument of <code>Alf.lispy</code>
|
690
|
-
and is responsible of resolving named datasets. A base class Environment::Folder
|
691
|
-
is provided with the Alf distribution, with a factory method on the Environment
|
692
|
-
class itself.
|
693
|
-
|
694
|
-
env = Alf::Environment.folder("path/to/a/folder")
|
695
|
-
|
696
|
-
An environment built that way will look for .rash and .alf files in the specified
|
697
|
-
folder and sub-folders. I'll of course strongly consider any contribution
|
698
|
-
implementing the Environment contract on top of SQL or NoSQL databases or anything
|
699
|
-
that can be useful to manipulate with relational algebra. Such contributions can
|
700
|
-
be added to the project directly. A base template would look like:
|
701
|
-
|
702
|
-
class Foo < Alf::Environment
|
703
|
-
|
704
|
-
#
|
705
|
-
# You should at least implement the _dataset_ method that resolves a
|
706
|
-
# name (a Symbol instance) to an Enumerable of tuples (typically a
|
707
|
-
# Reader). See Alf::Environment for exact contract details.
|
708
|
-
#
|
709
|
-
def dataset(name)
|
710
|
-
end
|
711
|
-
|
712
|
-
end
|
713
|
-
|
714
|
-
Read more about Environment's API so as to let your environment be recognized
|
715
|
-
in shell (--env=...) on rubydoc.info
|
716
|
-
|
717
|
-
### Adding file decoders, aka Readers
|
718
|
-
|
719
|
-
Environments should not be confused with Readers (see Reader class and its
|
720
|
-
subclasses). While the former resolve named datasets, the latter decode files
|
721
|
-
and/or other resources as tuple enumerables. Environments typically serve Reader
|
722
|
-
instances in response to dataset resolving.
|
723
|
-
|
724
|
-
Reader implementations decoding .rash and .alf files are provided in the main
|
725
|
-
alf.rb file. It's relatively easy to implement the Reader contract by extending
|
726
|
-
the Reader class and implementing an each method. Once again, contributions are
|
727
|
-
very welcome in lib/alf/reader (.csv files, .log files, and so on). A basic
|
728
|
-
template for this is as follows:
|
729
|
-
|
730
|
-
class Bar < Alf::Reader
|
731
|
-
|
732
|
-
#
|
733
|
-
# You should at least implement each, see Alf::Reader which provides a
|
734
|
-
# base implementation and a few tools
|
735
|
-
#
|
736
|
-
def each
|
737
|
-
# [...]
|
738
|
-
end
|
739
|
-
|
740
|
-
# By registering it, the Folder environment will automatically
|
741
|
-
# recognize and decode .bar files correctly!
|
742
|
-
Alf::Reader.register(:bar, [".bar"], self)
|
743
|
-
|
744
|
-
end
|
745
|
-
|
746
|
-
### Adding outputters, aka Renderers
|
747
|
-
|
748
|
-
Similarly, you can contribute renderers to output relations in html, or whatever
|
749
|
-
format you would consider interesting. See the Renderer class, and consider the
|
750
|
-
following template for contributions in lib/alf/renderer
|
751
|
-
|
752
|
-
class Glim < Alf::Renderer
|
753
|
-
|
754
|
-
#
|
755
|
-
# You should at least implement the execute method that renders tuples
|
756
|
-
# given in _input_ (an Enumerable of tuples) on the output buffer
|
757
|
-
# and returns the latter. See Alf::Renderer for the exact contract
|
758
|
-
# details.
|
759
|
-
#
|
760
|
-
def execute(output = $stdout)
|
761
|
-
# [...]
|
762
|
-
output
|
763
|
-
end
|
764
|
-
|
765
|
-
|
766
|
-
# By registering it, the output options of 'alf show' will
|
767
|
-
# automatically provide your --glim contribution
|
768
|
-
Alf::Renderer.register(:glim, "as a .glim file", self)
|
769
|
-
|
770
|
-
end
|
35
|
+
% alf --examples group suppliers -- size name status -- in_that_city
|
36
|
+
|
37
|
+
+--------+----------------------------+
|
38
|
+
| :city | :in_that_city |
|
39
|
+
+--------+----------------------------+
|
40
|
+
| London | +------+-------+---------+ |
|
41
|
+
| | | :sid | :name | :status | |
|
42
|
+
| | +------+-------+---------+ |
|
43
|
+
| | | S1 | Smith | 20 | |
|
44
|
+
| | | S4 | Clark | 20 | |
|
45
|
+
| | +------+-------+---------+ |
|
46
|
+
| Paris | +------+-------+---------+ |
|
47
|
+
| | | :sid | :name | :status | |
|
48
|
+
| | +------+-------+---------+ |
|
49
|
+
| | | S2 | Jones | 10 | |
|
50
|
+
| | | S3 | Blake | 30 | |
|
51
|
+
| | +------+-------+---------+ |
|
52
|
+
| Athens | +------+-------+---------+ |
|
53
|
+
| | | :sid | :name | :status | |
|
54
|
+
| | +------+-------+---------+ |
|
55
|
+
| | | S5 | Adams | 30 | |
|
56
|
+
| | +------+-------+---------+ |
|
57
|
+
+--------+----------------------------+
|
58
|
+
|
59
|
+
## Ruby Example
|
60
|
+
|
61
|
+
# Let get the same database in ruby
|
62
|
+
db = Alf.examples
|
63
|
+
|
64
|
+
# Group suppliers by city
|
65
|
+
grouped = db.query{
|
66
|
+
group(:suppliers, [:sid, :name, :status], :in_that_city)
|
67
|
+
}
|
68
|
+
# => same result as in shell
|
69
|
+
|
70
|
+
# Let make some computations on the sub-relations
|
71
|
+
db.query{
|
72
|
+
extend(grouped, how_many: ->{ in_that_city.count },
|
73
|
+
avg_status: ->{ in_that_city.avg{ status } })
|
74
|
+
}
|
75
|
+
# +--------+----------------------------+-----------+-------------+
|
76
|
+
# | :city | :in_that_city | :how_many | :avg_status |
|
77
|
+
# +--------+----------------------------+-----------+-------------+
|
78
|
+
# | London | +------+-------+---------+ | 2 | 20.000 |
|
79
|
+
# | | | :sid | :name | :status | | | |
|
80
|
+
# | | +------+-------+---------+ | | |
|
81
|
+
# | | | S1 | Smith | 20 | | | |
|
82
|
+
# | | | S4 | Clark | 20 | | | |
|
83
|
+
# | | +------+-------+---------+ | | |
|
84
|
+
# | Paris | +------+-------+---------+ | 2 | 20.000 |
|
85
|
+
# | | | :sid | :name | :status | | | |
|
86
|
+
# | | +------+-------+---------+ | | |
|
87
|
+
# | | | S2 | Jones | 10 | | | |
|
88
|
+
# | | | S3 | Blake | 30 | | | |
|
89
|
+
# | | +------+-------+---------+ | | |
|
90
|
+
# | Athens | +------+-------+---------+ | 1 | 30.000 |
|
91
|
+
# | | | :sid | :name | :status | | | |
|
92
|
+
# | | +------+-------+---------+ | | |
|
93
|
+
# | | | S5 | Adams | 30 | | | |
|
94
|
+
# | | +------+-------+---------+ | | |
|
95
|
+
# +--------+----------------------------+-----------+-------------+
|
96
|
+
|
97
|
+
# Now observe that the same result can also be expressed as follows (and can be
|
98
|
+
# optimized more easily)
|
99
|
+
summarized = db.query{
|
100
|
+
summary = summarize(:suppliers, [ :city ], how_many: count, avg_status: avg{ status })
|
101
|
+
join(grouped, summary)
|
102
|
+
}
|
103
|
+
|
104
|
+
# Oh, and of course...
|
105
|
+
require 'json'
|
106
|
+
puts summarized.to_json
|
107
|
+
# [{"city":"London","in_that_city":[{"sid":"S1","name":"Smith","status":20},{"sid":"S4"...
|
108
|
+
|
109
|
+
## Install, bundler, require
|
110
|
+
|
111
|
+
% [sudo] gem install alf [fastercsv, ...]
|
112
|
+
% alf --help
|
113
|
+
|
114
|
+
# API is not considered stable enough for now, please use
|
115
|
+
gem "alf", "= 0.13.0"
|
116
|
+
|
117
|
+
# The following should not break your code, but is a bit less safe,
|
118
|
+
# until 1.0.0 has been reached
|
119
|
+
gem "alf", "~> 0.13.0"
|
771
120
|
|
772
121
|
## Related Work & Tools
|
773
122
|
|
774
|
-
- You should certainly have a look at the
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
123
|
+
- You should certainly have a look at the
|
124
|
+
[Third Manifesto website](http://www.thethirdmanifesto.com/):
|
125
|
+
- Why not reading the
|
126
|
+
[third manifesto](http://www.dcs.warwick.ac.uk/~hugh/TTM/DBE-Chapter01.pdf) ?
|
127
|
+
- Also have a look at
|
128
|
+
[other implementation projects](http://www.dcs.warwick.ac.uk/~hugh/TTM/Projects.html)
|
129
|
+
especially [Rel](http://dbappbuilder.sourceforge.net/Rel.php) which provides an
|
130
|
+
implementation of the **Tutorial D** language.
|
131
|
+
- [Dan Kubb's Veritas](https://github.com/dkubb/veritas) project is worth considering
|
781
132
|
also in the Ruby community. While very similar to Alf in providing a pure ruby
|
782
133
|
algebra implementation, Veritas mostly provides a framework for manipulating
|
783
|
-
and statically analyzing algebra expressions so as to be able to
|
784
|
-
|
785
|
-
|
786
|
-
working together with Dan Kubb to see how Alf and Veritas could be closer from
|
787
|
-
each other in the future, if not in their codebase, at least in using the very
|
788
|
-
same terminology for the same concepts.
|
789
|
-
|
790
|
-
## Contributing
|
134
|
+
and statically analyzing algebra expressions so as to be able to
|
135
|
+
[optimize them](https://github.com/dkubb/veritas-optimizer) and
|
136
|
+
[compile them to SQL](https://github.com/dkubb/veritas-sql-generator).
|
791
137
|
|
792
|
-
|
138
|
+
## Contributing
|
793
139
|
|
794
|
-
You know the rules:
|
140
|
+
You know the rules:
|
795
141
|
|
796
142
|
* The code is on github https://github.com/blambeau/alf
|
797
143
|
* Please report any problem or bug in the issue tracker on github
|
@@ -800,67 +146,51 @@ You know the rules:
|
|
800
146
|
Alf is distributed under a MIT licence. Please let me know if it does not fit
|
801
147
|
your needs and I'll see what I can do!
|
802
148
|
|
803
|
-
|
804
|
-
|
805
|
-
Alf's code style is very inspired from what I've found in Sinatra when looking
|
806
|
-
at its internals a few months ago. Alf, as Sinatra, is mostly implemented in a
|
807
|
-
single file, lib/alf.rb. Everything is there except specific third-party contributions
|
808
|
-
(in lib/alf/...). You'll need an editor or IDE that supports code folding/unfolding.
|
809
|
-
Then, follow the guide:
|
810
|
-
|
811
|
-
1. Fold everything but the Alf module.
|
812
|
-
2. Main concepts, first level of abstraction, should fit on the screen
|
813
|
-
3. Unfold the concept you're interested in, and return to the previous bullet
|
814
|
-
|
815
|
-
### Roadmap
|
149
|
+
## Roadmap
|
816
150
|
|
817
151
|
Below is what I've imagined about Alf's future. However, this is to be interpreted
|
818
152
|
as my own wish list, while I would love hearing yours instead.
|
819
153
|
|
820
|
-
- Towards 1.0.0, I would like to stabilize and document Alf public APIs as well
|
154
|
+
- Towards 1.0.0, I would like to stabilize and document Alf public APIs as well
|
821
155
|
as internals (a few concepts are still unstable there). Alf also has a certain
|
822
156
|
number of limitations that are worth overcoming for version 1.0.0. The latter
|
823
|
-
include the semantically wrong way of applying joins on sub-relations, the
|
157
|
+
include the semantically wrong way of applying joins on sub-relations, the
|
824
158
|
impossibility to use Lispy expressions on sub-relations in extend, and the error
|
825
159
|
management which is unspecific and unfriendly so far.
|
826
|
-
- I also would like starting collecting Reader, Renderer and
|
827
|
-
contributions for common data sources (SQL, NoSQL, CSV, LOGS) and output
|
828
|
-
formats (HTML, XML, JSON). Contributions could be either developped as different
|
829
|
-
gem projects or distributed with Alf's gem and source code, I still need to
|
160
|
+
- I also would like starting collecting Reader, Renderer and Connection
|
161
|
+
contributions for common data sources (SQL, NoSQL, CSV, LOGS) and output
|
162
|
+
formats (HTML, XML, JSON). Contributions could be either developped as different
|
163
|
+
gem projects or distributed with Alf's gem and source code, I still need to
|
830
164
|
decide the exact policy (suggestions are more than welcome here)
|
831
165
|
- Alf will remain a practical tool before everything else. In the middle term,
|
832
166
|
I would like to complete the set of available operators (relational and non-
|
833
|
-
relational ones). Some of them will be operators described in D & D books
|
834
|
-
while others will be new suggestions of mine.
|
835
|
-
- In the long term Alf should be able to avoid loading tuples in memory (under
|
836
|
-
a certain number of conditions on datasources) for almost all queries.
|
837
|
-
- Without targetting a fast tool at all, I also would like Alf to provide a basic
|
838
|
-
optimizer that would be able to push equality restrictions down and materialize
|
839
|
-
sub-expressions used more than once in with expressions.
|
167
|
+
relational ones). Some of them will be operators described in D & D books
|
168
|
+
while others will be new suggestions of mine.
|
169
|
+
- In the long term Alf should be able to avoid loading tuples in memory (under
|
170
|
+
a certain number of conditions on datasources) for almost all queries.
|
171
|
+
- Without targetting a fast tool at all, I also would like Alf to provide a basic
|
172
|
+
optimizer that would be able to push equality restrictions down and materialize
|
173
|
+
sub-expressions used more than once in with expressions.
|
840
174
|
|
841
|
-
|
175
|
+
## Versioning policy
|
842
176
|
|
843
|
-
Alf respects
|
177
|
+
Alf respects [semantic versioning](http://semver.org/), which means that it has
|
844
178
|
a X.Y.Z version number and follows a few rules.
|
845
179
|
|
846
|
-
- The public API is made of the commandline tool, the Lispy dialect and the
|
847
|
-
Relation datastructure. This API will become stable with version 1.0.0 in a
|
180
|
+
- The public API is made of the commandline tool, the Lispy dialect and the
|
181
|
+
Relation datastructure. This API will become stable with version 1.0.0 in a
|
848
182
|
near future.
|
849
|
-
- Currently, version 1.0.0 **has not been reached**. It means that **anything
|
850
|
-
may change at any time**. Best effort will be done to upgrade Y when backward
|
183
|
+
- Currently, version 1.0.0 **has not been reached**. It means that **anything
|
184
|
+
may change at any time**. Best effort will be done to upgrade Y when backward
|
851
185
|
incompatible changes occur.
|
852
186
|
- Once 1.0.0 will be reached, the following rules will be followed:
|
853
|
-
- Backward compatible bug fixes will increase Z.
|
854
|
-
- New features and enhancements that do not break backward compatibility of
|
187
|
+
- Backward compatible bug fixes will increase Z.
|
188
|
+
- New features and enhancements that do not break backward compatibility of
|
855
189
|
the public API will increase the Y number.
|
856
|
-
- Non backward compatible changes of the public API will increase the X
|
190
|
+
- Non backward compatible changes of the public API will increase the X
|
857
191
|
number.
|
858
192
|
|
859
|
-
All classes and modules but Alf module, the Lispy DSL and Alf::Relation are part
|
193
|
+
All classes and modules but Alf module, the Lispy DSL and Alf::Relation are part
|
860
194
|
of the private API and may change at any time. A best-effort strategy is followed
|
861
195
|
to avoid breaking internals on tiny (Z) version increases, especially extension
|
862
|
-
points like Reader and Renderer.
|
863
|
-
|
864
|
-
## Enjoy Alf!
|
865
|
-
|
866
|
-
- No problem dude!
|
196
|
+
points like Reader and Renderer.
|