alf 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. data/CHANGELOG.md +255 -129
  2. data/Gemfile +31 -1
  3. data/Gemfile.lock +17 -20
  4. data/LICENCE.md +1 -1
  5. data/Manifest.txt +2 -0
  6. data/README.md +37 -43
  7. data/TODO.md +1 -1
  8. data/alf.gemspec +10 -7
  9. data/alf.noespec +24 -13
  10. data/bin/alf +2 -2
  11. data/doc/commands/exec.md +16 -0
  12. data/doc/commands/help.md +11 -0
  13. data/doc/commands/main.md +33 -0
  14. data/doc/commands/show.md +19 -0
  15. data/doc/operators/non_relational/autonum.md +23 -0
  16. data/doc/operators/non_relational/clip.md +31 -0
  17. data/doc/operators/non_relational/coerce.md +15 -0
  18. data/doc/operators/non_relational/compact.md +20 -0
  19. data/doc/operators/non_relational/defaults.md +32 -0
  20. data/doc/operators/non_relational/generator.md +20 -0
  21. data/doc/operators/non_relational/sort.md +24 -0
  22. data/doc/operators/relational/extend.md +18 -0
  23. data/doc/operators/relational/group.md +27 -0
  24. data/doc/operators/relational/intersect.md +13 -0
  25. data/doc/operators/relational/join.md +27 -0
  26. data/doc/operators/relational/matching.md +20 -0
  27. data/doc/operators/relational/minus.md +12 -0
  28. data/doc/operators/relational/not-matching.md +20 -0
  29. data/doc/operators/relational/project.md +28 -0
  30. data/doc/operators/relational/quota.md +21 -0
  31. data/doc/operators/relational/rank.md +27 -0
  32. data/doc/operators/relational/rename.md +17 -0
  33. data/doc/operators/relational/restrict.md +25 -0
  34. data/doc/operators/relational/summarize.md +25 -0
  35. data/doc/operators/relational/ungroup.md +20 -0
  36. data/doc/operators/relational/union.md +14 -0
  37. data/doc/operators/relational/unwrap.md +20 -0
  38. data/doc/operators/relational/wrap.md +24 -0
  39. data/examples/csv/suppliers.csv +6 -0
  40. data/examples/logs/access.log +1000 -0
  41. data/examples/logs/combined.alf +2 -0
  42. data/examples/logs/hits.alf +14 -0
  43. data/examples/logs/not_found.alf +7 -0
  44. data/examples/logs/robots-cheating.alf +11 -0
  45. data/examples/logs/robots.alf +8 -0
  46. data/examples/northwind/customers.csv +92 -0
  47. data/examples/northwind/northwind.db +0 -0
  48. data/examples/northwind/orders.csv +831 -0
  49. data/examples/operators/clip.alf +1 -1
  50. data/examples/operators/database.alf +5 -6
  51. data/examples/operators/defaults.alf +1 -1
  52. data/examples/operators/group.alf +1 -1
  53. data/examples/operators/project.alf +2 -1
  54. data/examples/operators/pseudo-with.alf +2 -2
  55. data/examples/operators/quota.alf +2 -2
  56. data/examples/operators/summarize.alf +2 -2
  57. data/lib/alf/aggregator/aggregators.rb +77 -0
  58. data/lib/alf/aggregator/base.rb +95 -0
  59. data/lib/alf/aggregator/class_methods.rb +57 -0
  60. data/lib/alf/buffer/sorted.rb +48 -0
  61. data/lib/alf/command/class_methods.rb +27 -0
  62. data/lib/alf/command/doc_manager.rb +72 -0
  63. data/lib/alf/command/exec.rb +12 -0
  64. data/lib/alf/command/help.rb +31 -0
  65. data/lib/alf/command/main.rb +146 -0
  66. data/lib/alf/command/show.rb +33 -0
  67. data/lib/alf/environment/base.rb +37 -0
  68. data/lib/alf/environment/class_methods.rb +93 -0
  69. data/lib/alf/environment/explicit.rb +38 -0
  70. data/lib/alf/environment/folder.rb +62 -0
  71. data/lib/alf/extra/csv.rb +104 -0
  72. data/lib/alf/extra/logs.rb +100 -0
  73. data/lib/alf/extra/sequel.rb +77 -0
  74. data/lib/alf/{yaml.rb → extra/yaml.rb} +0 -0
  75. data/lib/alf/extra.rb +5 -0
  76. data/lib/alf/iterator/base.rb +38 -0
  77. data/lib/alf/iterator/class_methods.rb +22 -0
  78. data/lib/alf/iterator/proxy.rb +33 -0
  79. data/lib/alf/lispy/instance_methods.rb +157 -0
  80. data/lib/alf/operator/base.rb +74 -0
  81. data/lib/alf/operator/binary.rb +32 -0
  82. data/lib/alf/operator/cesure.rb +45 -0
  83. data/lib/alf/operator/class_methods.rb +132 -0
  84. data/lib/alf/operator/experimental.rb +9 -0
  85. data/lib/alf/operator/non_relational/autonum.rb +24 -0
  86. data/lib/alf/operator/non_relational/clip.rb +20 -0
  87. data/lib/alf/operator/non_relational/coerce.rb +21 -0
  88. data/lib/alf/operator/non_relational/compact.rb +62 -0
  89. data/lib/alf/operator/non_relational/defaults.rb +25 -0
  90. data/lib/alf/operator/non_relational/generator.rb +38 -0
  91. data/lib/alf/operator/non_relational/sort.rb +23 -0
  92. data/lib/alf/operator/nullary.rb +20 -0
  93. data/lib/alf/operator/relational/extend.rb +24 -0
  94. data/lib/alf/operator/relational/group.rb +32 -0
  95. data/lib/alf/operator/relational/intersect.rb +37 -0
  96. data/lib/alf/operator/relational/join.rb +106 -0
  97. data/lib/alf/operator/relational/matching.rb +45 -0
  98. data/lib/alf/operator/relational/minus.rb +37 -0
  99. data/lib/alf/operator/relational/not_matching.rb +45 -0
  100. data/lib/alf/operator/relational/project.rb +22 -0
  101. data/lib/alf/operator/relational/quota.rb +51 -0
  102. data/lib/alf/operator/relational/rank.rb +55 -0
  103. data/lib/alf/operator/relational/rename.rb +19 -0
  104. data/lib/alf/operator/relational/restrict.rb +20 -0
  105. data/lib/alf/operator/relational/summarize.rb +83 -0
  106. data/lib/alf/operator/relational/ungroup.rb +25 -0
  107. data/lib/alf/operator/relational/union.rb +32 -0
  108. data/lib/alf/operator/relational/unwrap.rb +21 -0
  109. data/lib/alf/operator/relational/wrap.rb +22 -0
  110. data/lib/alf/operator/shortcut.rb +53 -0
  111. data/lib/alf/operator/signature.rb +262 -0
  112. data/lib/alf/operator/transform.rb +27 -0
  113. data/lib/alf/operator/unary.rb +38 -0
  114. data/lib/alf/reader/alf_file.rb +24 -0
  115. data/lib/alf/reader/base.rb +119 -0
  116. data/lib/alf/reader/class_methods.rb +82 -0
  117. data/lib/alf/reader/rash.rb +28 -0
  118. data/lib/alf/relation/class_methods.rb +37 -0
  119. data/lib/alf/relation/instance_methods.rb +127 -0
  120. data/lib/alf/renderer/base.rb +72 -0
  121. data/lib/alf/renderer/class_methods.rb +58 -0
  122. data/lib/alf/renderer/rash.rb +19 -0
  123. data/lib/alf/{text.rb → renderer/text.rb} +1 -1
  124. data/lib/alf/tools/coerce.rb +14 -0
  125. data/lib/alf/tools/miscellaneous.rb +77 -0
  126. data/lib/alf/tools/to_lispy.rb +99 -0
  127. data/lib/alf/tools/to_ruby_literal.rb +14 -0
  128. data/lib/alf/tools/tuple_handle.rb +50 -0
  129. data/lib/alf/types/attr_list.rb +56 -0
  130. data/lib/alf/types/attr_name.rb +28 -0
  131. data/lib/alf/types/boolean.rb +12 -0
  132. data/lib/alf/types/heading.rb +96 -0
  133. data/lib/alf/types/ordering.rb +93 -0
  134. data/lib/alf/types/renaming.rb +57 -0
  135. data/lib/alf/types/summarization.rb +76 -0
  136. data/lib/alf/types/tuple_computation.rb +61 -0
  137. data/lib/alf/types/tuple_expression.rb +61 -0
  138. data/lib/alf/types/tuple_predicate.rb +49 -0
  139. data/lib/alf/version.rb +2 -2
  140. data/lib/alf.rb +193 -3714
  141. data/spec/integration/__database__/group.alf +1 -1
  142. data/spec/integration/__database__/suppliers_csv.csv +6 -0
  143. data/spec/integration/command/alf/alf.db +0 -0
  144. data/spec/integration/command/alf/alf_env_sqlite.cmd +1 -0
  145. data/spec/integration/command/alf/alf_env_sqlite.stdout +9 -0
  146. data/spec/integration/command/alf/alf_help.cmd +1 -0
  147. data/spec/integration/command/alf/alf_help.stdout +67 -0
  148. data/spec/integration/command/autonum/autonum_0.cmd +1 -1
  149. data/spec/integration/command/coerce/coerce_1.cmd +1 -0
  150. data/spec/integration/command/coerce/coerce_1.stdout +5 -0
  151. data/spec/integration/command/defaults/defaults_0.cmd +1 -1
  152. data/spec/integration/command/defaults/defaults_0.stdout +9 -9
  153. data/spec/integration/command/defaults/defaults_2.cmd +1 -0
  154. data/spec/integration/command/defaults/defaults_2.stdout +9 -0
  155. data/spec/integration/command/generator/generator_1.cmd +1 -0
  156. data/spec/integration/command/generator/generator_1.stdout +10 -0
  157. data/spec/integration/command/generator/generator_2.cmd +1 -0
  158. data/spec/integration/command/generator/generator_2.stdout +5 -0
  159. data/spec/integration/command/generator/generator_3.cmd +1 -0
  160. data/spec/integration/command/generator/generator_3.stdout +5 -0
  161. data/spec/integration/command/group/group_0.cmd +1 -1
  162. data/spec/integration/command/group/group_1.cmd +1 -1
  163. data/spec/integration/command/help/help_1.cmd +1 -0
  164. data/spec/integration/command/help/help_1.stdout +22 -0
  165. data/spec/integration/command/quota/quota_0.cmd +1 -1
  166. data/spec/integration/command/rank/rank_1.cmd +1 -1
  167. data/spec/integration/command/rank/rank_1.stdout +10 -10
  168. data/spec/integration/command/rank/rank_2.cmd +1 -1
  169. data/spec/integration/command/rank/rank_2.stdout +10 -10
  170. data/spec/integration/command/rank/rank_3.cmd +1 -1
  171. data/spec/integration/command/rank/rank_3.stdout +10 -10
  172. data/spec/integration/command/rank/rank_4.cmd +1 -1
  173. data/spec/integration/command/rank/rank_5.cmd +1 -1
  174. data/spec/integration/command/show/show_csv.cmd +1 -0
  175. data/spec/integration/command/show/show_csv.stdout +6 -0
  176. data/spec/integration/command/show/show_rash_2.cmd +1 -1
  177. data/spec/integration/command/show/show_rash_2.stdout +5 -5
  178. data/spec/integration/command/sort/sort_0.cmd +1 -1
  179. data/spec/integration/command/sort/sort_1.cmd +1 -1
  180. data/spec/integration/command/sort/sort_1.stdout +2 -2
  181. data/spec/integration/command/sort/sort_2.cmd +1 -0
  182. data/spec/integration/command/sort/sort_2.stdout +9 -0
  183. data/spec/integration/command/sort/sort_3.cmd +1 -0
  184. data/spec/integration/command/sort/sort_3.stdout +9 -0
  185. data/spec/integration/command/summarize/summarize_0.cmd +1 -1
  186. data/spec/integration/command/ungroup/ungroup_0.cmd +1 -1
  187. data/spec/integration/command/wrap/wrap_0.cmd +1 -1
  188. data/spec/integration/semantics/test_project.alf +5 -6
  189. data/spec/integration/semantics/test_rank.alf +16 -16
  190. data/spec/integration/test_command.rb +17 -6
  191. data/spec/integration/test_examples.rb +1 -1
  192. data/spec/regression/logs/apache_combined.log +5 -0
  193. data/spec/regression/logs/test_path_attribute.rb +25 -0
  194. data/spec/regression/relation/test_relation_allbut_all.rb +14 -0
  195. data/spec/shared/an_operator_class.rb +10 -5
  196. data/spec/spec_helper.rb +1 -7
  197. data/spec/unit/assumptions/test_set.rb +64 -0
  198. data/spec/unit/command/doc_manager/dynamic.md +1 -0
  199. data/spec/unit/command/doc_manager/example.md +1 -0
  200. data/spec/unit/command/doc_manager/example_1.txt +11 -0
  201. data/spec/unit/command/doc_manager/static.md +1 -0
  202. data/spec/unit/command/doc_manager/test_call.rb +49 -0
  203. data/spec/unit/csv/input.csv +3 -0
  204. data/spec/unit/csv/test_reader.rb +66 -0
  205. data/spec/unit/csv/test_renderer.rb +73 -0
  206. data/spec/unit/lispy/test_relation.rb +37 -0
  207. data/spec/unit/lispy/test_run.rb +40 -0
  208. data/spec/unit/lispy/test_tuple.rb +36 -0
  209. data/spec/unit/logs/apache_combined.log +5 -0
  210. data/spec/unit/logs/postgresql.log +29 -0
  211. data/spec/unit/logs/test_reader.rb +56 -0
  212. data/spec/unit/operator/non_relational/compact/{buffer_based.rb → test_buffer_based.rb} +0 -0
  213. data/spec/unit/operator/non_relational/test_clip.rb +1 -1
  214. data/spec/unit/operator/non_relational/test_coerce.rb +35 -0
  215. data/spec/unit/operator/non_relational/test_defaults.rb +15 -2
  216. data/spec/unit/operator/non_relational/test_generator.rb +78 -0
  217. data/spec/unit/operator/relational/join/test_hash_based.rb +4 -4
  218. data/spec/unit/operator/relational/matching/test_hash_based.rb +6 -6
  219. data/spec/unit/operator/relational/not_matching/test_hash_based.rb +4 -4
  220. data/spec/unit/operator/relational/summarize/test_hash_based.rb +10 -6
  221. data/spec/unit/operator/relational/summarize/test_sort_based.rb +18 -7
  222. data/spec/unit/operator/relational/test_group.rb +8 -8
  223. data/spec/unit/operator/relational/test_intersect.rb +3 -3
  224. data/spec/unit/operator/relational/test_minus.rb +3 -3
  225. data/spec/unit/operator/relational/test_project.rb +12 -2
  226. data/spec/unit/operator/relational/test_quota.rb +5 -6
  227. data/spec/unit/operator/relational/test_summarize.rb +9 -11
  228. data/spec/unit/operator/relational/test_union.rb +1 -1
  229. data/spec/unit/operator/relational/test_wrap.rb +1 -1
  230. data/spec/unit/operator/signature/test_collect_on.rb +45 -0
  231. data/spec/unit/operator/signature/test_initialize.rb +17 -0
  232. data/spec/unit/operator/signature/test_install.rb +56 -0
  233. data/spec/unit/operator/signature/test_option_parser.rb +36 -0
  234. data/spec/unit/operator/signature/test_parse_args.rb +60 -0
  235. data/spec/unit/operator/signature/test_parse_argv.rb +87 -0
  236. data/spec/unit/operator/signature/test_to_lispy.rb +102 -0
  237. data/spec/unit/operator/signature/test_to_shell.rb +103 -0
  238. data/spec/unit/operator/test_non_relational.rb +3 -1
  239. data/spec/unit/relation/test_relops.rb +20 -15
  240. data/spec/unit/sequel/alf.db +0 -0
  241. data/spec/unit/sequel/test_environment.rb +54 -0
  242. data/spec/unit/test_aggregator.rb +32 -22
  243. data/spec/unit/test_environment.rb +5 -0
  244. data/spec/unit/test_lispy.rb +4 -0
  245. data/spec/unit/test_relation.rb +5 -0
  246. data/spec/unit/text/test_cell.rb +6 -6
  247. data/spec/unit/text/test_row.rb +3 -3
  248. data/spec/unit/text/test_table.rb +6 -6
  249. data/spec/unit/tools/test_coalesce.rb +15 -0
  250. data/spec/unit/tools/test_coerce.rb +10 -0
  251. data/spec/unit/tools/test_to_lispy.rb +138 -0
  252. data/spec/unit/tools/test_to_ruby_literal.rb +10 -0
  253. data/spec/unit/tools/test_tuple_handle.rb +1 -59
  254. data/spec/unit/types/test_attr_list.rb +106 -0
  255. data/spec/unit/types/test_attr_name.rb +52 -0
  256. data/spec/unit/{test_heading.rb → types/test_heading.rb} +10 -0
  257. data/spec/unit/types/test_ordering.rb +127 -0
  258. data/spec/unit/types/test_renaming.rb +55 -0
  259. data/spec/unit/types/test_summarization.rb +63 -0
  260. data/spec/unit/types/test_tuple_computation.rb +60 -0
  261. data/spec/unit/types/test_tuple_expression.rb +64 -0
  262. data/spec/unit/types/test_tuple_predicate.rb +79 -0
  263. data/tasks/debug_mail.rake +1 -1
  264. data/tasks/debug_mail.txt +5 -0
  265. data/tasks/gh-pages.rake +63 -0
  266. metadata +325 -52
  267. data/spec/unit/operator/test_command_methods.rb +0 -38
  268. data/spec/unit/tools/test_ordering_key.rb +0 -94
  269. data/spec/unit/tools/test_parse_commandline_args.rb +0 -47
  270. data/spec/unit/tools/test_projection_key.rb +0 -83
@@ -0,0 +1,127 @@
1
+ module Alf
2
+ class Relation
3
+ module InstanceMethods
4
+
5
+ protected
6
+
7
+ # @return [Set] the set of tuples
8
+ attr_reader :tuples
9
+
10
+ public
11
+
12
+ #
13
+ # Creates a Relation instance.
14
+ #
15
+ # @param [Set] tuples a set of tuples
16
+ #
17
+ def initialize(tuples)
18
+ raise ArgumentError unless tuples.is_a?(Set)
19
+ @tuples = tuples
20
+ end
21
+
22
+ #
23
+ # (see Iterator#each)
24
+ #
25
+ def each(&block)
26
+ tuples.each(&block)
27
+ end
28
+
29
+ #
30
+ # Returns relation's cardinality (number of tuples).
31
+ #
32
+ # @return [Integer] relation's cardinality
33
+ #
34
+ def cardinality
35
+ tuples.size
36
+ end
37
+ alias :size :cardinality
38
+ alias :count :cardinality
39
+
40
+ # Returns true if this relation is empty
41
+ def empty?
42
+ cardinality == 0
43
+ end
44
+
45
+ #
46
+ # Install the DSL through iteration over defined operators
47
+ #
48
+ Operator::each do |op_class|
49
+ meth_name = Tools.ruby_case(Tools.class_name(op_class)).to_sym
50
+ if op_class.unary?
51
+ define_method(meth_name) do |*args|
52
+ op = op_class.new(*args).pipe(self)
53
+ Relation.coerce(op)
54
+ end
55
+ elsif op_class.binary?
56
+ define_method(meth_name) do |right, *args|
57
+ op = op_class.new(*args).pipe([self, Iterator.coerce(right)])
58
+ Relation.coerce(op)
59
+ end
60
+ elsif op_class.nullary?
61
+ define_method(meth_name) do |*args|
62
+ op = op_class.new(*args)
63
+ Relation.coerce(op)
64
+ end
65
+ else
66
+ raise "Unexpected operator #{op_class}"
67
+ end
68
+ end # Operators::each
69
+
70
+ alias :+ :union
71
+ alias :- :minus
72
+
73
+ # Shortcut for project(attributes, :allbut => true)
74
+ def allbut(attributes)
75
+ project(attributes, :allbut => true)
76
+ end
77
+
78
+ #
79
+ # (see Object#hash)
80
+ #
81
+ def hash
82
+ @tuples.hash
83
+ end
84
+
85
+ #
86
+ # (see Object#==)
87
+ #
88
+ def ==(other)
89
+ return nil unless other.is_a?(Relation)
90
+ other.tuples == self.tuples
91
+ end
92
+ alias :eql? :==
93
+
94
+ #
95
+ # Returns a textual representation of this relation
96
+ #
97
+ def to_s
98
+ Alf::Renderer.text(self).execute("")
99
+ end
100
+
101
+ #
102
+ # Returns an array with all tuples in this relation.
103
+ #
104
+ # @param [Tools::Ordering] an optional ordering key (any argument
105
+ # recognized by Ordering.coerce is supported here).
106
+ # @return [Array] an array of hashes, in requested order (if specified)
107
+ #
108
+ def to_a(okey = nil)
109
+ okey = Tools.coerce(okey, Ordering) if okey
110
+ ary = tuples.to_a
111
+ ary.sort!(&okey.sorter) if okey
112
+ ary
113
+ end
114
+
115
+ #
116
+ # Returns a literal representation of this relation
117
+ #
118
+ def to_ruby_literal
119
+ "Alf::Relation[" +
120
+ tuples.collect{|t| Tools.to_ruby_literal(t)}.join(', ') + "]"
121
+ end
122
+ alias :inspect :to_ruby_literal
123
+
124
+ end # module InstanceMethods
125
+ include(InstanceMethods)
126
+ end # class Relation
127
+ end # module Alf
@@ -0,0 +1,72 @@
1
+ module Alf
2
+ class Renderer
3
+ module Base
4
+
5
+ # Default renderer options
6
+ DEFAULT_OPTIONS = {}
7
+
8
+ # Renderer input (typically an Iterator)
9
+ attr_accessor :input
10
+
11
+ # @return [Environment] Optional wired environment
12
+ attr_accessor :environment
13
+
14
+ # @return [Hash] Renderer's options
15
+ attr_accessor :options
16
+
17
+ #
18
+ # Creates a reader instance.
19
+ #
20
+ # @param [Iterator] iterator an Iterator of tuples to render
21
+ # @param [Environment] environment wired environment, serving this reader
22
+ # @param [Hash] options Reader's options (see doc of subclasses)
23
+ #
24
+ def initialize(*args)
25
+ @input, @environment, @options = case args.first
26
+ when Array
27
+ Tools.varargs(args, [Array, Environment, Hash])
28
+ else
29
+ Tools.varargs(args, [Iterator, Environment, Hash])
30
+ end
31
+ @options = self.class.const_get(:DEFAULT_OPTIONS).merge(@options || {})
32
+ end
33
+
34
+ #
35
+ # Sets the renderer input.
36
+ #
37
+ # This method mimics {Iterator#pipe} and have the same contract.
38
+ #
39
+ def pipe(input, env = environment)
40
+ self.environment = env
41
+ self.input = input
42
+ self
43
+ end
44
+
45
+ #
46
+ # Executes the rendering, outputting the resulting tuples on the provided
47
+ # output buffer.
48
+ #
49
+ # The default implementation simply coerces the input as an Iterator and
50
+ # delegates the call to {#render}.
51
+ #
52
+ def execute(output = $stdout)
53
+ render(Iterator.coerce(input, environment), output)
54
+ end
55
+
56
+ protected
57
+
58
+ #
59
+ # Renders tuples served by the iterator to the output buffer provided and
60
+ # returns the latter.
61
+ #
62
+ # This method must be implemented by subclasses unless {#execute} is
63
+ # overriden.
64
+ #
65
+ def render(iterator, output)
66
+ end
67
+ undef :render
68
+
69
+ end # module Base
70
+ include Base
71
+ end # class Renderer
72
+ end # module Alf
@@ -0,0 +1,58 @@
1
+ module Alf
2
+ class Renderer
3
+ module ClassMethods
4
+
5
+ #
6
+ # Returns registered renderers
7
+ #
8
+ def renderers
9
+ @renderers ||= []
10
+ end
11
+
12
+ #
13
+ # Register a renderering class with a given name and description.
14
+ #
15
+ # Registered class must at least provide a constructor with an empty
16
+ # signature. The name must be a symbol which can safely be used as a ruby
17
+ # method name. A factory class method of that name and degelation signature
18
+ # is automatically installed on the Renderer class.
19
+ #
20
+ # @param [Symbol] name a name for the output format
21
+ # @param [String] description an output format description (for 'alf show')
22
+ # @param [Class] clazz Renderer subclass used to render in this format
23
+ #
24
+ def register(name, description, clazz)
25
+ renderers << [name, description, clazz]
26
+ (class << self; self; end).
27
+ send(:define_method, name) do |*args|
28
+ clazz.new(*args)
29
+ end
30
+ end
31
+
32
+ #
33
+ # Returns a Renderer instance for the given output format name.
34
+ #
35
+ # @param [Symbol] name name of an output format previously registered
36
+ # @param [...] args other arguments to pass to the renderer constructor
37
+ # @return [Renderer] a Renderer instance, already wired if args are
38
+ # provided
39
+ #
40
+ def renderer(name, *args)
41
+ if r = renderers.find{|triple| triple[0] == name}
42
+ r[2].new(*args)
43
+ else
44
+ raise "No renderer registered for #{name}"
45
+ end
46
+ end
47
+
48
+ #
49
+ # Yields each (name,description,clazz) previously registered in turn
50
+ #
51
+ def each_renderer
52
+ renderers.each(&Proc.new)
53
+ end
54
+
55
+ end # module ClassMethods
56
+ extend(ClassMethods)
57
+ end # class Renderer
58
+ end # module Alf
@@ -0,0 +1,19 @@
1
+ module Alf
2
+ class Renderer
3
+ #
4
+ # Implements the Renderer contract through inspect
5
+ #
6
+ class Rash < Renderer
7
+
8
+ # (see Renderer#render)
9
+ def render(input, output)
10
+ input.each do |tuple|
11
+ output << Tools.to_ruby_literal(tuple) << "\n"
12
+ end
13
+ output
14
+ end
15
+
16
+ Renderer.register(:rash, "as ruby hashes", self)
17
+ end # class Rash
18
+ end # class Renderer
19
+ end # module Alf
@@ -52,7 +52,7 @@ module Alf
52
52
  when Symbol
53
53
  value.inspect
54
54
  when Float
55
- "%.7f" % value
55
+ "%.3f" % value
56
56
  when Hash
57
57
  value.inspect
58
58
  when Alf::Iterator
@@ -0,0 +1,14 @@
1
+ module Alf
2
+ module Tools
3
+
4
+ # Coercion rules
5
+ Coercions = Myrrha::Coerce.dup.append do
6
+ end
7
+
8
+ # Delegated to Coercions
9
+ def coerce(value, domain)
10
+ Coercions.apply(value, domain)
11
+ end
12
+
13
+ end # module Tools
14
+ end # module Alf
@@ -0,0 +1,77 @@
1
+ module Alf
2
+ module Tools
3
+
4
+
5
+ # Helper to define methods with multiple signatures.
6
+ #
7
+ # Example:
8
+ #
9
+ # varargs([1, "hello"], [Integer, String]) # => [1, "hello"]
10
+ # varargs(["hello"], [Integer, String]) # => [nil, "hello"]
11
+ #
12
+ def varargs(args, types)
13
+ types.collect{|t| t===args.first ? args.shift : nil}
14
+ end
15
+
16
+ #
17
+ # Attempt to require(who) the most friendly way as possible.
18
+ #
19
+ def friendly_require(who, dep = nil, retried = false)
20
+ gem(who, dep) if dep && defined?(Gem)
21
+ require who
22
+ rescue LoadError => ex
23
+ if retried
24
+ raise "Unable to require #{who}, which is now needed\n"\
25
+ "Try 'gem install #{who}'"
26
+ else
27
+ require 'rubygems' unless defined?(Gem)
28
+ friendly_require(who, dep, true)
29
+ end
30
+ end
31
+
32
+ # Returns the unqualified name of a ruby class or module
33
+ #
34
+ # Example
35
+ #
36
+ # class_name(Alf::Tools) -> :Tools
37
+ #
38
+ def class_name(clazz)
39
+ clazz.name.to_s =~ /([A-Za-z0-9_]+)$/
40
+ $1.to_sym
41
+ end
42
+
43
+ #
44
+ # Converts an unqualified class or module name to a ruby case method name.
45
+ #
46
+ # Example
47
+ #
48
+ # ruby_case(:Alf) -> "alf"
49
+ # ruby_case(:HelloWorld) -> "hello_world"
50
+ #
51
+ def ruby_case(s)
52
+ s.to_s.gsub(/[A-Z]/){|x| "_#{x.downcase}"}[1..-1]
53
+ end
54
+
55
+ #
56
+ # Returns the first non nil values from arguments
57
+ #
58
+ # Example
59
+ #
60
+ # coalesce(nil, 1, "abc") -> 1
61
+ #
62
+ def coalesce(*args)
63
+ found = args.find{|x| !x.nil?}
64
+ found.nil? ? (block_given? ? yield : nil) : found
65
+ end
66
+
67
+ #
68
+ # Iterates over enum and yields the block on each element.
69
+ # Collect block results as key/value pairs returns them as
70
+ # a Hash.
71
+ #
72
+ def tuple_collect(enum)
73
+ Hash[enum.collect{|elm| yield(elm)}]
74
+ end
75
+
76
+ end # module Tools
77
+ end # module Alf
@@ -0,0 +1,99 @@
1
+ module Alf
2
+ module Tools
3
+
4
+ # Myrrha rules for converting to ruby literals
5
+ ToLispy = Myrrha::coercions do |r|
6
+
7
+ # Delegate to #to_lispy if it exists
8
+ lispy_able = lambda{|v,rd| v.respond_to?(:to_lispy)}
9
+ r.upon(lispy_able) do |v,rd|
10
+ v.to_lispy
11
+ end
12
+
13
+ # On AttrList
14
+ r.upon(Types::AttrList) do |v, rd|
15
+ Tools.to_ruby_literal(v.attributes)
16
+ end
17
+
18
+ # On Heading
19
+ r.upon(Types::Heading) do |v, rd|
20
+ Tools.to_ruby_literal(v.attributes)
21
+ end
22
+
23
+ # On Ordering
24
+ r.upon(Types::Ordering) do |v, rd|
25
+ Tools.to_ruby_literal(v.ordering)
26
+ end
27
+
28
+ # On Renaming
29
+ r.upon(Types::Renaming) do |v, rd|
30
+ Tools.to_ruby_literal(v.renaming)
31
+ end
32
+
33
+ # On Renaming
34
+ r.upon(lambda{|v,rd| Iterator::Proxy === v}) do |v, rd|
35
+ Tools.to_ruby_literal(v.dataset)
36
+ end
37
+
38
+ # On TupleExpression
39
+ r.upon(Types::TupleExpression) do |v, rd|
40
+ unless src = v.source
41
+ raise NotImplementedError, "TupleExpression #{v} has no source"
42
+ end
43
+ "->(){ #{src} }"
44
+ end
45
+
46
+ # On TuplePredicate
47
+ r.upon(Types::TuplePredicate) do |v, rd|
48
+ unless src = v.source
49
+ raise NotImplementedError, "TuplePredicate #{v} has no source"
50
+ end
51
+ "->(){ #{src} }"
52
+ end
53
+
54
+ # On TupleComputation
55
+ r.upon(Types::TupleComputation) do |v, rd|
56
+ "{" + v.computation.collect{|name,compu|
57
+ [name.inspect, r.coerce(compu)].join(" => ")
58
+ }.join(', ') + "}"
59
+ end
60
+
61
+ # On Aggregator
62
+ r.upon(lambda{|v,_| Aggregator === v}) do |v, rd|
63
+ unless src = v.source
64
+ raise NotImplementedError, "Aggregator #{v} has no source"
65
+ end
66
+ src
67
+ end
68
+
69
+ # On Summarization
70
+ r.upon(Types::Summarization) do |v, rd|
71
+ "{" + v.aggregations.collect{|name,compu|
72
+ [name.inspect, r.coerce(compu)].join(" => ")
73
+ }.join(', ') + "}"
74
+ end
75
+
76
+ # On Command and Operator
77
+ cmd = lambda{|v,_| (Command === v) || (Operator === v)}
78
+ r.upon(cmd) do |v,rd|
79
+ cmdname = v.class.command_name.to_s.gsub('-', '_')
80
+ oper, args, opts = v.class.signature.collect_on(v)
81
+ args = opts.empty? ? (oper + args) : (oper + args + [ opts ])
82
+ args = args.collect{|arg| r.coerce(arg)}
83
+ "(#{cmdname} #{args.join(', ')})"
84
+ end
85
+
86
+ # Let's assume to to_ruby_literal will make the job
87
+ r.fallback(Object) do |v, _|
88
+ Tools.to_ruby_literal(v)
89
+ end
90
+
91
+ end
92
+
93
+ # Delegated to ToLispy
94
+ def to_lispy(value)
95
+ ToLispy.apply(value)
96
+ end
97
+
98
+ end # module Tools
99
+ end # module Alf
@@ -0,0 +1,14 @@
1
+ module Alf
2
+ module Tools
3
+
4
+ # Myrrha rules for converting to ruby literals
5
+ ToRubyLiteral = Myrrha::ToRubyLiteral.dup.append do
6
+ end
7
+
8
+ # Delegated to ToRubyLiteral
9
+ def to_ruby_literal(value)
10
+ ToRubyLiteral.apply(value)
11
+ end
12
+
13
+ end # module Tools
14
+ end # module Alf
@@ -0,0 +1,50 @@
1
+ module Alf
2
+ module Tools
3
+ #
4
+ # Provides a handle, implementing a flyweight design pattern on tuples.
5
+ #
6
+ class TupleHandle
7
+
8
+ # Creates an handle instance
9
+ def initialize
10
+ @tuple = nil
11
+ end
12
+
13
+ #
14
+ # Sets the next tuple to use.
15
+ #
16
+ # This method installs the handle as a side effect
17
+ # on first call.
18
+ #
19
+ def set(tuple)
20
+ build(tuple) if @tuple.nil?
21
+ @tuple = tuple
22
+ self
23
+ end
24
+
25
+ #
26
+ # Evaluates a tuple expression on the current tuple.
27
+ #
28
+ def evaluate(expr)
29
+ TupleExpression.coerce(expr).evaluate(self)
30
+ end
31
+
32
+ private
33
+
34
+ #
35
+ # Builds this handle with a tuple.
36
+ #
37
+ # This method should be called only once and installs
38
+ # instance methods on the handle with keys of _tuple_.
39
+ #
40
+ def build(tuple)
41
+ tuple.keys.each do |k|
42
+ (class << self; self; end).send(:define_method, k) do
43
+ @tuple[k]
44
+ end
45
+ end
46
+ end
47
+
48
+ end # class TupleHandle
49
+ end # module Tools
50
+ end # module Alf
@@ -0,0 +1,56 @@
1
+ module Alf
2
+ module Types
3
+ #
4
+ # Defines a projection key
5
+ #
6
+ class AttrList
7
+
8
+ # Projection attributes
9
+ attr_accessor :attributes
10
+
11
+ def initialize(attributes)
12
+ @attributes = attributes
13
+ end
14
+
15
+ def self.coerce(arg)
16
+ case arg
17
+ when AttrList
18
+ arg
19
+ when Ordering
20
+ AttrList.new(arg.attributes)
21
+ when Array
22
+ AttrList.new(arg.collect{|s| Tools.coerce(s, AttrName)})
23
+ else
24
+ raise ArgumentError, "Unable to coerce #{arg} to a projection key"
25
+ end
26
+ end
27
+
28
+ def self.from_argv(argv, opts = {})
29
+ coerce(argv)
30
+ end
31
+
32
+ def to_ordering
33
+ Ordering.new attributes.collect{|arg| [arg, :asc]}
34
+ end
35
+
36
+ def project(tuple, allbut = false)
37
+ split(tuple, allbut).first
38
+ end
39
+
40
+ def split(tuple, allbut = false)
41
+ projection, rest = {}, tuple.dup
42
+ attributes.each do |a|
43
+ projection[a] = tuple[a]
44
+ rest.delete(a)
45
+ end
46
+ allbut ? [rest, projection] : [projection, rest]
47
+ end
48
+
49
+ def ==(other)
50
+ other.is_a?(AttrList) && (other.attributes == attributes)
51
+ end
52
+ alias :eql? :==
53
+
54
+ end # class AttrList
55
+ end # module Types
56
+ end # module Alf
@@ -0,0 +1,28 @@
1
+ module Alf
2
+ module Types
3
+ #
4
+ # Data type for being a valid attribute name
5
+ #
6
+ class AttrName < Symbol
7
+ extend Myrrha::Domain
8
+
9
+ def self.predicate
10
+ @predicate ||= lambda{|s| s.to_s =~ /^[a-zA-Z0-9_]+$/}
11
+ end
12
+
13
+ def self.coerce(arg)
14
+ if arg.respond_to?(:to_sym)
15
+ sym = arg.to_sym
16
+ return sym if self.===(sym)
17
+ end
18
+ raise ArgumentError, "Unable to coerce `#{arg.inspect}` to AttrName"
19
+ end
20
+
21
+ def self.from_argv(argv, opts = {})
22
+ raise ArgumentError if argv.size > 1
23
+ coerce(argv.first || opts[:default])
24
+ end
25
+
26
+ end # class AttrName
27
+ end # module Types
28
+ end # module Alf
@@ -0,0 +1,12 @@
1
+ module Alf
2
+ module Types
3
+
4
+ Boolean = Myrrha::Boolean
5
+
6
+ def Boolean.from_argv(argv, opts={})
7
+ raise ArgumentError if argv.size > 1
8
+ Tools.coerce(argv.first, Boolean)
9
+ end
10
+
11
+ end
12
+ end