alf 0.12.2 → 0.13.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 +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
|
-
[](https://gemnasium.com/blambeau/alf)
|
|
3
|
+
Relational Algebra at your fingertips
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
[](http://travis-ci.org/alf-tool/alf)
|
|
6
|
+
[](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.
|