fancy 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +5 -4
  3. data/bin/fspec +19 -1
  4. data/bin/ifancy +139 -35
  5. data/boot/README +2 -9
  6. data/boot/extconf.rb +0 -1
  7. data/boot/fancy_ext/module.rb +5 -15
  8. data/boot/fancy_ext/thread.rb +22 -9
  9. data/boot/rbx-compiler/README +0 -4
  10. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  11. data/boot/rbx-compiler/parser/parser.y +1 -0
  12. data/doc/api/fancy.css +1 -6
  13. data/doc/api/fancy.jsonp +1 -1
  14. data/doc/api/fdoc.js +2 -4
  15. data/doc/api/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/doc/api/images/ui-bg_flat_0_eeeeee_40x100.png +0 -0
  17. data/doc/api/images/ui-bg_flat_55_ffffff_40x100.png +0 -0
  18. data/doc/api/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  19. data/doc/api/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  20. data/doc/api/images/ui-bg_highlight-soft_100_f6f6f6_1x100.png +0 -0
  21. data/doc/api/images/ui-bg_highlight-soft_25_0073ea_1x100.png +0 -0
  22. data/doc/api/images/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
  23. data/doc/api/images/ui-icons_0073ea_256x240.png +0 -0
  24. data/doc/api/images/ui-icons_454545_256x240.png +0 -0
  25. data/doc/api/images/ui-icons_666666_256x240.png +0 -0
  26. data/doc/api/images/ui-icons_ff0084_256x240.png +0 -0
  27. data/doc/api/images/ui-icons_ffffff_256x240.png +0 -0
  28. data/doc/api/index.html +5 -4
  29. data/doc/api/jquery-1.8.2.min.js +2 -0
  30. data/doc/api/jquery-ui-1.9.0.custom.min.css +5 -0
  31. data/doc/api/jquery-ui-1.9.0.custom.min.js +6 -0
  32. data/doc/features.md +8 -3
  33. data/examples/argv.fy +1 -1
  34. data/examples/closures.fy +1 -4
  35. data/examples/echo.fy +2 -2
  36. data/examples/guess_number.fy +18 -0
  37. data/examples/nested_classes.fy +3 -15
  38. data/lib/argv.fy +23 -18
  39. data/lib/array.fy +18 -37
  40. data/lib/block.fy +125 -0
  41. data/lib/boot.fy +1 -0
  42. data/lib/compiler/ast/block.fy +1 -1
  43. data/lib/compiler/ast/identifier.fy +1 -1
  44. data/lib/compiler/ast/message_send.fy +0 -13
  45. data/lib/compiler/ast/method_def.fy +1 -1
  46. data/lib/compiler/ast/singleton_method_def.fy +1 -0
  47. data/lib/compiler/ast/tuple_literal.fy +1 -1
  48. data/lib/compiler/command.fy +1 -1
  49. data/lib/compiler/compiler.fy +8 -6
  50. data/lib/contracts.fy +1 -1
  51. data/lib/directory.fy +1 -1
  52. data/lib/dynamic_slot_object.fy +1 -1
  53. data/lib/enumerable.fy +316 -25
  54. data/lib/enumerator.fy +11 -8
  55. data/lib/eval.fy +0 -3
  56. data/lib/fancy_spec.fy +27 -0
  57. data/lib/fdoc.fy +8 -8
  58. data/lib/file.fy +25 -1
  59. data/lib/hash.fy +91 -0
  60. data/lib/html.fy +40 -11
  61. data/lib/integer.fy +4 -0
  62. data/lib/main.fy +18 -11
  63. data/lib/object.fy +33 -7
  64. data/lib/option_parser.fy +20 -1
  65. data/lib/package/dependency.fy +8 -0
  66. data/lib/package/dependency_installer.fy +3 -6
  67. data/lib/package/handler.fy +4 -4
  68. data/lib/package/installer.fy +2 -5
  69. data/lib/package/list.fy +3 -4
  70. data/lib/parser/ext/parser.y +1 -0
  71. data/lib/proxies.fy +0 -2
  72. data/lib/queue.fy +7 -0
  73. data/lib/rbx.fy +1 -0
  74. data/lib/rbx/actor.fy +3 -1
  75. data/lib/rbx/alpha.fy +24 -0
  76. data/lib/rbx/array.fy +3 -1
  77. data/lib/rbx/class.fy +5 -8
  78. data/lib/rbx/date_time.fy +14 -0
  79. data/lib/rbx/file.fy +6 -0
  80. data/lib/rbx/hash.fy +42 -0
  81. data/lib/rbx/thread.fy +5 -7
  82. data/lib/string.fy +56 -4
  83. data/lib/symbol.fy +29 -1
  84. data/lib/time.fy +17 -0
  85. data/lib/vars.fy +4 -3
  86. data/lib/version.fy +1 -1
  87. data/ruby_lib/interactive/hilight.rb +125 -0
  88. data/tests/array.fy +19 -7
  89. data/tests/block.fy +103 -4
  90. data/tests/class.fy +31 -26
  91. data/tests/control_flow.fy +0 -1
  92. data/tests/dynamic_key_hash.fy +22 -1
  93. data/tests/enumerable.fy +239 -7
  94. data/tests/enumerator.fy +7 -0
  95. data/tests/file.fy +16 -0
  96. data/tests/future.fy +1 -11
  97. data/tests/future_proxy.fy +8 -0
  98. data/tests/hash.fy +132 -9
  99. data/tests/html.fy +30 -13
  100. data/tests/integer.fy +3 -0
  101. data/tests/method.fy +6 -11
  102. data/tests/object.fy +12 -5
  103. data/tests/option_parser.fy +12 -3
  104. data/tests/string.fy +69 -1
  105. data/tests/symbol.fy +24 -0
  106. metadata +42 -12
  107. data/boot/rsexp_pretty_printer.rb +0 -76
  108. data/doc/api/jquery-ui.min.js +0 -401
  109. data/doc/api/jquery.tools.min.js +0 -192
  110. data/doc/api/themeswitchertool.js +0 -250
  111. data/examples/future_sends.fy +0 -15
data/lib/boot.fy CHANGED
@@ -42,6 +42,7 @@ require: "future"
42
42
  require: "struct"
43
43
  require: "message_sink"
44
44
  require: "kvo"
45
+ require: "time"
45
46
 
46
47
  # version holds fancy's version number
47
48
  require: "version"
@@ -14,7 +14,7 @@ class Fancy AST {
14
14
  @arguments prelude=(nil)
15
15
  }
16
16
 
17
- if: (@args.total_args > 1) then: {
17
+ if: (@args total_args > 1) then: {
18
18
  @arguments prelude=('multi)
19
19
  }
20
20
 
@@ -148,7 +148,7 @@ class Fancy AST {
148
148
  thread bytecode: g
149
149
  g send('current, 0, false)
150
150
  @varname bytecode: g
151
- g send(':[], 1, false)
151
+ g send('dynamic_var:, 1, false)
152
152
  }
153
153
  }
154
154
  }
@@ -42,19 +42,6 @@ class Fancy AST {
42
42
  return nil
43
43
  }
44
44
 
45
- # check if we might have a block invocation using block(x,y) syntax.
46
- if: ruby_send? then: {
47
- if: (@receiver is_a?: Self) then: {
48
- if: (g state() scope() search_local(@name name)) then: {
49
- @name bytecode: g
50
- args = ArrayLiteral new: @line array: (@args args)
51
- args bytecode: g
52
- g send('call:, 1, false)
53
- return nil
54
- }
55
- }
56
- }
57
-
58
45
  @receiver bytecode: g
59
46
  @args bytecode: g
60
47
  pos(g)
@@ -83,7 +83,7 @@ class Fancy AST {
83
83
 
84
84
  class MethodArgs : Rubinius AST FormalArguments {
85
85
  def initialize: @line args: @array ([]) {
86
- initialize(@line, @array map() |a| { a to_sym() }, nil, nil)
86
+ initialize(@line, @array map: @{ to_sym }, nil, nil)
87
87
  }
88
88
  }
89
89
  }
@@ -13,6 +13,7 @@ class Fancy AST {
13
13
 
14
14
  class SingletonMethodDefScope : Rubinius AST DefineSingletonScope {
15
15
  def initialize: @line name: @name args: @arguments body: @body {
16
+ { @body = ExpressionList new: @line } unless: @body
16
17
  if: (@body empty?) then: {
17
18
  @body unshift_expression: $ NilLiteral new: @line
18
19
  }
@@ -17,7 +17,7 @@ class Fancy AST {
17
17
 
18
18
  ms bytecode: g
19
19
 
20
- @elements each_with_index() |e i| {
20
+ @elements each_with_index: |e i| {
21
21
  g dup()
22
22
  ary = [FixnumLiteral new: @line value: i, e]
23
23
 
@@ -33,7 +33,7 @@ class Fancy {
33
33
  size = argv size()
34
34
  files = "file"
35
35
  { files = files + "s" } if: (size > 1)
36
- "Compiled " ++ (argv size()) ++ " " ++ files ++ "." . println
36
+ "Compiled " ++ (argv size) ++ " " ++ files ++ "." . println
37
37
  }
38
38
  }
39
39
 
@@ -3,12 +3,12 @@ class Fancy {
3
3
  read_write_slots: [ 'parser, 'generator, 'packager, 'writer ]
4
4
 
5
5
  def self compiled_name: file {
6
- """
7
- Returns a the compiled filename for @file
6
+ """
7
+ Returns a the compiled filename for @file
8
8
 
9
- If file ends with .fy extention the will return with .fyc extension
10
- Otherwise it will append .compiled.fyc to the filename.
11
- """
9
+ If file ends with .fy extention the will return with .fyc extension
10
+ Otherwise it will append .compiled.fyc to the filename.
11
+ """
12
12
 
13
13
  if: (file suffix?(".fy")) then: {
14
14
  file + "c"
@@ -18,7 +18,9 @@ class Fancy {
18
18
  }
19
19
 
20
20
  def self from: first_stage to: last_stage {
21
- "Creates a new compiler from @first_stage to @last_stage"
21
+ """
22
+ Creates a new compiler from @first_stage to @last_stage.
23
+ """
22
24
 
23
25
  new(first_stage, last_stage)
24
26
  }
data/lib/contracts.fy CHANGED
@@ -57,7 +57,7 @@ class Class {
57
57
  """
58
58
 
59
59
  pim = Set new: $ provided_interface_methods map: @{ message_name to_s } + instance_methods
60
- methods all?: |m| { pim includes?: (m message_name to_s) }
60
+ methods to_a all?: |m| { pim includes?: (m message_name to_s) }
61
61
  }
62
62
 
63
63
  def missing_methods_for_interface: methods {
data/lib/directory.fy CHANGED
@@ -12,6 +12,6 @@ class Directory {
12
12
  Indicates, if a Directory exists with a given pathname.
13
13
  """
14
14
 
15
- (File exists?: dirname) and: (File directory?: dirname)
15
+ (File exists?: dirname) && { File directory?: dirname }
16
16
  }
17
17
  }
@@ -65,7 +65,7 @@ class DynamicKeyHash : Fancy BasicObject {
65
65
  val = p[idx]
66
66
  if: @deep then: {
67
67
  match val {
68
- case Block -> val = val to_hash
68
+ case Block -> val = val to_hash_deep
69
69
  }
70
70
  }
71
71
  @hash[slotname to_sym]: val
data/lib/enumerable.fy CHANGED
@@ -42,6 +42,7 @@ class Fancy {
42
42
  """
43
43
  @return The first element in the @Fancy::Enumerable@.
44
44
  """
45
+
45
46
  at: 0
46
47
  }
47
48
 
@@ -49,6 +50,7 @@ class Fancy {
49
50
  """
50
51
  @return The second element in the @Fancy::Enumerable@.
51
52
  """
53
+
52
54
  at: 1
53
55
  }
54
56
 
@@ -56,6 +58,7 @@ class Fancy {
56
58
  """
57
59
  @return The third element in the @Fancy::Enumerable@.
58
60
  """
61
+
59
62
  at: 2
60
63
  }
61
64
 
@@ -63,6 +66,7 @@ class Fancy {
63
66
  """
64
67
  @return The fourth element in the @Fancy::Enumerable@.
65
68
  """
69
+
66
70
  at: 3
67
71
  }
68
72
 
@@ -136,6 +140,15 @@ class Fancy {
136
140
  """
137
141
  Similar to @each:@ but calls an additional @Block@ between
138
142
  calling the first @Block@ for each element in self.
143
+
144
+ Example:
145
+ result = \"\"
146
+ [1,2,3,4,5] each: |i| {
147
+ result << i
148
+ } in_between: {
149
+ result << \"-\"
150
+ }
151
+ result # => \"1-2-3-4-5\"
139
152
  """
140
153
 
141
154
  between = { between = between_block }
@@ -152,6 +165,7 @@ class Fancy {
152
165
 
153
166
  Joins a collection with a @String@ between each element, returning a new @String@.
154
167
 
168
+ Example:
155
169
  \"hello, world\" join: \"-\" # => \"h-e-l-l-o-,- -w-o-r-l-d\"
156
170
  """
157
171
 
@@ -172,7 +186,7 @@ class Fancy {
172
186
  Works similar to @Fancy::Enumerable#inject:into:@ but uses first element as value injected.
173
187
 
174
188
  Example:
175
- (1,2,3) reduce_by: '+ # => same as: (2,3) inject: 1 into: '+
189
+ (1,2,3) join_by: '+ # => same as: (2,3) inject: 1 into: '+
176
190
  """
177
191
 
178
192
  first, *rest = self
@@ -232,6 +246,79 @@ class Fancy {
232
246
  }
233
247
  }
234
248
 
249
+ def find: item do: block {
250
+ """
251
+ @item Item to find in @self.
252
+ @block @Block@ to be called with @item if found in @self.
253
+
254
+ Calls @block with @item, if found.
255
+ If @item is not in @self, @block is not called.
256
+ """
257
+
258
+ if: (find: item) then: block
259
+ }
260
+
261
+ def find_with_index: item do: block {
262
+ """
263
+ @item Item to find in @self.
264
+ @block @Block@ to be called with @item and its index in @self.
265
+
266
+ Calls @block with @item and its index in @self, if found.
267
+ If @item is not in @self, @block is not called.
268
+ """
269
+
270
+ for_every: item with_index_do: |x i| {
271
+ return block call: [x, i]
272
+ }
273
+ nil
274
+ }
275
+
276
+ def for_every: item with_index_do: block {
277
+ """
278
+ @item Item to call @block with.
279
+ @block @Block@ to be called with @item and each of its indexes in @self.
280
+
281
+ Calls @block with @item and each of its indexes in @self, if @item is in @self.
282
+ """
283
+
284
+ each_with_index: |x i| {
285
+ if: (item == x) then: {
286
+ block call: [x, i]
287
+ }
288
+ }
289
+ }
290
+
291
+ def for_every: item do: block {
292
+ """
293
+ @item Item to call @block with.
294
+ @block @Block@ to be called with @item for every occurance of @item in @self.
295
+
296
+ Calls @block with @item for each occurance of @item in @self.
297
+
298
+ Example:
299
+ count = 0
300
+ [1,2,3,2,1] for_every: 1 do: { count = count + 1 }
301
+ # count is now 2
302
+ """
303
+
304
+ each: |x| {
305
+ if: (item == x) then: { block call: [x] }
306
+ }
307
+ }
308
+
309
+ def last_index_of: item {
310
+ """
311
+ @item Item for which the last index in @self should be found.
312
+ @return Last index of @item in @self, or @nil (if not in @self).
313
+
314
+ Returns the last index for @item in @self, or @nil, if @item is not in @self.
315
+ """
316
+
317
+ last_idx = nil
318
+ for_every: item with_index_do: |_ i| { last_idx = i }
319
+ last_idx
320
+ }
321
+
235
322
  def find_by: block {
236
323
  """
237
324
  Similar to @find:@ but takes a block that is called for each element to find it.
@@ -292,6 +379,14 @@ class Fancy {
292
379
  }
293
380
  }
294
381
 
382
+ def flat_map: block {
383
+ """
384
+ Similar to @Fancy::Enumerable#map:@ but returns the result @Array@ flattened.
385
+ """
386
+
387
+ map: block . tap: @{ flatten! }
388
+ }
389
+
295
390
  def select: condition {
296
391
  """
297
392
  @condition A @Block@ that is used as a filter on all elements in @self.
@@ -307,6 +402,24 @@ class Fancy {
307
402
  coll
308
403
  }
309
404
 
405
+ def select_with_index: condition {
406
+ """
407
+ @condition A @Block@ that is used as a filter on all elements in @self.
408
+ @return An @Array@ containing all elements and their indices in @self that yield @true when called with @condition.
409
+
410
+ Returns a new @Array@ with all elements and their indices that meet the given
411
+ condition block. @condition is called with each element and its index in @self.
412
+ """
413
+
414
+ tmp = []
415
+ each_with_index: |obj idx| {
416
+ if: (condition call: [obj, idx]) then: {
417
+ tmp << [obj, idx]
418
+ }
419
+ }
420
+ tmp
421
+ }
422
+
310
423
  def reject: condition {
311
424
  """
312
425
  Similar to @select:@ but inverse.
@@ -354,14 +467,14 @@ class Fancy {
354
467
  """
355
468
 
356
469
  coll = []
357
- drop = nil
358
- first_check = true
470
+ drop? = false
471
+ first_check? = true
359
472
  each: |x| {
360
- if: (drop or: first_check) then: {
361
- drop = condition call: [x]
362
- first_check = nil
473
+ if: (drop? or: first_check?) then: {
474
+ drop? = condition call: [x]
475
+ first_check? = false
363
476
  # check, if we actually have to insert this one:
364
- unless: drop do: {
477
+ unless: drop? do: {
365
478
  coll << x
366
479
  }
367
480
  } else: {
@@ -405,6 +518,18 @@ class Fancy {
405
518
 
406
519
  alias_method: 'skip: for: 'drop:
407
520
 
521
+ def drop_last: amount (1) {
522
+ """
523
+ @amount Amount of elements to drop from the end.
524
+ @return New @Array@ without last @amount elements.
525
+
526
+ Example:
527
+ [1,2,3,4] drop_last: 2 # => [1,2]
528
+ """
529
+
530
+ first: (size - amount)
531
+ }
532
+
408
533
  def reduce: block init_val: init_val {
409
534
  """
410
535
  Calculates a value based on a given block to be called on an accumulator
@@ -416,7 +541,7 @@ class Fancy {
416
541
 
417
542
  acc = init_val
418
543
  each: |x| {
419
- acc = (block call: [acc, x])
544
+ acc = block call: [acc, x]
420
545
  }
421
546
  acc
422
547
  }
@@ -507,18 +632,19 @@ class Fancy {
507
632
  [[1,2], [2,3,4], [-1]] superior_by: '< taking: 'first # => [-1]
508
633
  """
509
634
 
635
+ { return nil } if: empty?
510
636
 
511
- pairs = self map: |val| {
512
- (val, selection_block call: [val])
513
- }
637
+ retval = first
638
+ retval_cmp = selection_block call: [retval]
514
639
 
515
- retval = pairs first
516
- pairs each: |p| {
517
- if: (comparison_block call: [p second, retval second]) then: {
518
- retval = p
640
+ rest each: |p| {
641
+ cmp = selection_block call: [p]
642
+ if: (comparison_block call: [cmp, retval_cmp]) then: {
643
+ retval = p
644
+ retval_cmp = cmp
519
645
  }
520
646
  }
521
- retval first
647
+ retval
522
648
  }
523
649
 
524
650
  def max {
@@ -531,6 +657,20 @@ class Fancy {
531
657
  superior_by: '>
532
658
  }
533
659
 
660
+ def max_by: block {
661
+ """
662
+ @block @Block@ by which to calculate the maximum value in @self.
663
+ @return Maximum value in @self based on @block.
664
+
665
+ Returns the maximum value in the Enumerable (via the '>' comparison message).
666
+
667
+ Example:
668
+ [[1,2,3], [1,2], [1]] max_by: @{ size } # => [1,2,3]
669
+ """
670
+
671
+ superior_by: '> taking: block
672
+ }
673
+
534
674
  def min {
535
675
  """
536
676
  @return Minimum value in @self.
@@ -541,6 +681,57 @@ class Fancy {
541
681
  superior_by: '<
542
682
  }
543
683
 
684
+ def min_by: block {
685
+ """
686
+ @block @Block@ by which to calculate the minimum value in @self.
687
+ @return Minimum value in @self based on @block.
688
+
689
+ Returns the minimum value in the Enumerable (via the '<' comparison message).
690
+
691
+ Example:
692
+ [[1,2,3], [1,2], [1]] min_by: @{ size } # => [1]
693
+ """
694
+
695
+ superior_by: '< taking: block
696
+ }
697
+
698
+ def min_max {
699
+ """
700
+ @return @Tuple@ of min and max value in @self.
701
+
702
+ If @self is empty, returns (nil, nil).
703
+
704
+ Example:
705
+ (1,2,3,4) min_max # => (1, 3)
706
+ """
707
+
708
+ min_max_by: @{ identity }
709
+ }
710
+
711
+ def min_max_by: block {
712
+ """
713
+ @block @Block@ to calculate the min and max value by.
714
+ @return @Tuple@ of min and max value based on @block in @self.
715
+
716
+ Calls @block with each element in @self to determine min and max values.
717
+ If @self is empty, returns (nil, nil).
718
+
719
+ Example:
720
+ (\"a\", \”bc\", \”def\") min_max_by: 'size # => (1, 3)
721
+ """
722
+
723
+ min, max = nil, nil
724
+ min_val, max_val = nil, nil
725
+ each: |x| {
726
+ val = block call: [x]
727
+ { min = val; min_val = x } unless: min
728
+ { min = val; min_val = x } if: (val < min)
729
+ { max = val; max_val = x } unless: max
730
+ { max = val; max_val = x } if: (val > max)
731
+ }
732
+ (min_val, max_val)
733
+ }
734
+
544
735
  def sum {
545
736
  """
546
737
  Calculates the sum of all the elements in the @Enumerable
@@ -564,7 +755,7 @@ class Fancy {
564
755
  @return Average value in @self (expecting @Number@s or Objects implementing @+ and @*).
565
756
  """
566
757
 
567
- { return 0 } if: (size == 0)
758
+ { return 0 } if: empty?
568
759
  sum to_f / size
569
760
  }
570
761
 
@@ -593,6 +784,40 @@ class Fancy {
593
784
  coll
594
785
  }
595
786
 
787
+ def chunk_by: block {
788
+ """
789
+ @block @Block@ to chunk @self by.
790
+ @return @Array@ of chunks, each including the return value of calling @block with elements in the chunk, as well as the elements themselves (within another @Array@).
791
+
792
+ Similar to @Fancy::Enumerable#partition_by:@ but includes the value of
793
+ calling @block with an element within the chunk.
794
+
795
+ Example:
796
+ [1,3,4,5,6,8,10] chunk_by: 'odd?
797
+ # => [[true, [1,3]], [false, [4]], [true, [5]], [false, [6,8,10]]]
798
+ """
799
+
800
+ { return [] } if: empty?
801
+
802
+ chunks = []
803
+ curr_chunk = []
804
+ initial = first
805
+ last_val = block call: [initial]
806
+ curr_chunk << initial
807
+
808
+ rest each: |x| {
809
+ val = block call: [x]
810
+ if: (val != last_val) then: {
811
+ chunks << [last_val, curr_chunk]
812
+ curr_chunk = []
813
+ }
814
+ curr_chunk << x
815
+ last_val = val
816
+ }
817
+ { chunks << [last_val, curr_chunk] } unless: $ curr_chunk empty?
818
+ chunks
819
+ }
820
+
596
821
  def random {
597
822
  """
598
823
  @return Random element in @self.
@@ -601,20 +826,32 @@ class Fancy {
601
826
  at: $ size random
602
827
  }
603
828
 
604
- def sort_by: block {
829
+ def sort: comparison_block {
605
830
  """
606
- @block @Block@ taking 2 arguments used to compare elements in a collection.
831
+ @comparison_block @Block@ taking 2 arguments used to compare elements in a collection.
607
832
  @return Sorted @Array@ of elements in @self.
608
833
 
609
834
  Sorts a collection by a given comparison block.
610
835
  """
611
836
 
612
- if: (block is_a?: Symbol) then: {
613
- sort() |a b| {
614
- a receive_message: block . <=> (b receive_message: block)
615
- }
616
- } else: {
617
- sort(&block)
837
+ sort(&comparison_block)
838
+ }
839
+
840
+ def sort_by: block {
841
+ """
842
+ @block @Block@ taking 1 argument used to extract a value to use for comparison.
843
+ @return Sorted @Array@ of elements in @self based on @block.
844
+
845
+ Sorts a collection by calling a @Block@ with every element
846
+ and using the return values for comparison.
847
+
848
+ Example:
849
+ [\"abc\", \"abcd\", \"ab\", \"a\", \"\"] sort_by: @{ size }
850
+ # => [\"\", \"a\", \"ab\", \"abc\", \"abcd\"]
851
+ """
852
+
853
+ sort_by() |x| {
854
+ block call: [x]
618
855
  }
619
856
  }
620
857
 
@@ -650,6 +887,27 @@ class Fancy {
650
887
  groups
651
888
  }
652
889
 
890
+ def group_by: block {
891
+ """
892
+ @block @Block@ used to group elements in @self by.
893
+ @return @Hash@ with elements in @self grouped by return value of calling @block with them.
894
+
895
+ Returns the elements grouped by @block in a @Hash@ (the keys being the
896
+ value of calling @block with the elements).
897
+
898
+ Example:
899
+ ('foo, 1, 2, 'bar) group_by: @{ class }
900
+ # => <[Symbol => ['foo, 'bar], Fixnum => [1,2]]>
901
+ """
902
+
903
+ h = <[]>
904
+ each: |x| {
905
+ group = h at: (block call: [x]) else_put: { [] }
906
+ group << x
907
+ }
908
+ h
909
+ }
910
+
653
911
  def reverse {
654
912
  """
655
913
  @return @self in reverse order.
@@ -800,5 +1058,38 @@ class Fancy {
800
1058
  }
801
1059
  result
802
1060
  }
1061
+
1062
+ def one?: block {
1063
+ """
1064
+ @block @Block@ to be used to check for a condition expected only once in @self.
1065
+ @return @true if @block yields @true only once for all elements in @self.
1066
+
1067
+ Example:
1068
+ (0,1,2) one?: 'odd? # => true
1069
+ (0,1,2) one?: 'even? # => false
1070
+ """
1071
+
1072
+ got_one? = false
1073
+ each: |x| {
1074
+ if: (block call: [x]) then: {
1075
+ { return false } if: got_one?
1076
+ got_one? = true
1077
+ }
1078
+ }
1079
+ return got_one?
1080
+ }
1081
+
1082
+ def none?: block {
1083
+ """
1084
+ @block @Block@ to be used to check for a condition expected not once in @self.
1085
+ @return @true if none of the elements in @self called with @block yield @true.
1086
+
1087
+ Example:
1088
+ (0,2,4) none?: 'odd? # => true
1089
+ (0,2,5) none?: 'odd? # => false
1090
+ """
1091
+
1092
+ any?: block . not
1093
+ }
803
1094
  }
804
1095
  }