fancy 0.7.0 → 0.8.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 (165) hide show
  1. data/README.md +38 -86
  2. data/bin/fdoc +2 -22
  3. data/bin/fspec +8 -3
  4. data/bin/ifancy +1 -1
  5. data/boot/fancy_ext.rb +1 -0
  6. data/boot/fancy_ext/array.rb +19 -0
  7. data/boot/fancy_ext/class.rb +2 -4
  8. data/boot/fancy_ext/module.rb +2 -0
  9. data/boot/fancy_ext/object.rb +0 -17
  10. data/boot/rbx-compiler/compiler/ast/method_def.rb +0 -4
  11. data/boot/rbx-compiler/compiler/ast/singleton_method_def.rb +0 -7
  12. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  13. data/boot/rbx-compiler/parser/fancy_parser.c +1 -0
  14. data/doc/api/fancy.css +10 -1
  15. data/doc/api/fancy.jsonp +1 -1
  16. data/doc/api/fdoc.js +22 -9
  17. data/doc/api/octocat.png +0 -0
  18. data/doc/features.md +1 -2
  19. data/examples/actors.fy +1 -2
  20. data/examples/armstrong_numbers.fy +7 -3
  21. data/examples/blocks.fy +3 -3
  22. data/examples/distributing_proxy.fy +31 -0
  23. data/examples/future_sends.fy +15 -0
  24. data/examples/person.fy +1 -2
  25. data/lib/argv.fy +1 -7
  26. data/lib/array.fy +7 -11
  27. data/lib/block.fy +15 -0
  28. data/lib/boot.fy +4 -3
  29. data/lib/class.fy +354 -10
  30. data/lib/compiler.fy +1 -1
  31. data/lib/compiler/ast/assign.fy +4 -8
  32. data/lib/compiler/ast/async_send.fy +1 -2
  33. data/lib/compiler/ast/block.fy +5 -0
  34. data/lib/compiler/ast/class_def.fy +2 -1
  35. data/lib/compiler/ast/expression_list.fy +1 -2
  36. data/lib/compiler/ast/future_send.fy +1 -2
  37. data/lib/compiler/ast/identifier.fy +34 -17
  38. data/lib/compiler/ast/literals.fy +31 -19
  39. data/lib/compiler/ast/match.fy +5 -4
  40. data/lib/compiler/ast/message_send.fy +3 -5
  41. data/lib/compiler/ast/method_def.fy +0 -3
  42. data/lib/compiler/ast/range.fy +2 -4
  43. data/lib/compiler/ast/return.fy +2 -4
  44. data/lib/compiler/ast/script.fy +2 -4
  45. data/lib/compiler/ast/singleton_method_def.fy +0 -3
  46. data/lib/compiler/ast/string_interpolation.fy +2 -2
  47. data/lib/compiler/ast/super.fy +2 -4
  48. data/lib/compiler/ast/try_catch.fy +13 -9
  49. data/lib/compiler/ast/tuple_literal.fy +1 -2
  50. data/lib/compiler/compiler.fy +2 -2
  51. data/lib/compiler/stages.fy +3 -6
  52. data/lib/contracts.fy +89 -57
  53. data/lib/dynamic_slot_object.fy +21 -3
  54. data/lib/enumerable.fy +140 -4
  55. data/lib/enumerator.fy +1 -1
  56. data/lib/eval.fy +23 -9
  57. data/lib/exception.fy +16 -0
  58. data/lib/false_class.fy +36 -5
  59. data/lib/fancy_spec.fy +64 -34
  60. data/lib/fdoc.fy +85 -24
  61. data/lib/file.fy +19 -0
  62. data/lib/future.fy +4 -46
  63. data/lib/hash.fy +113 -0
  64. data/lib/integer.fy +25 -6
  65. data/lib/iteration.fy +3 -3
  66. data/lib/main.fy +5 -0
  67. data/lib/matchers.fy +79 -0
  68. data/lib/nil_class.fy +8 -0
  69. data/lib/object.fy +109 -18
  70. data/lib/option_parser.fy +118 -0
  71. data/lib/package/dependency.fy +4 -8
  72. data/lib/package/dependency_installer.fy +1 -1
  73. data/lib/package/handler.fy +6 -0
  74. data/lib/package/installer.fy +43 -16
  75. data/lib/package/list.fy +1 -2
  76. data/lib/package/specification.fy +5 -5
  77. data/lib/package/uninstaller.fy +9 -2
  78. data/lib/parser.fy +1 -3
  79. data/lib/parser/ext/ext.c +1 -0
  80. data/lib/parser/ext/lexer.lex +5 -0
  81. data/lib/parser/methods.fy +48 -46
  82. data/lib/proxies.fy +151 -0
  83. data/lib/rbx.fy +1 -0
  84. data/lib/rbx/actor.fy +16 -18
  85. data/lib/rbx/array.fy +18 -3
  86. data/lib/rbx/block.fy +1 -7
  87. data/lib/rbx/class.fy +54 -9
  88. data/lib/rbx/code_loader.fy +2 -5
  89. data/lib/rbx/compiled_method.fy +31 -0
  90. data/lib/rbx/debugger.fy +66 -0
  91. data/lib/rbx/directory.fy +8 -3
  92. data/lib/rbx/documentation.fy +1 -1
  93. data/lib/rbx/file.fy +22 -0
  94. data/lib/rbx/integer.fy +1 -1
  95. data/lib/rbx/match_data.fy +2 -1
  96. data/lib/rbx/method.fy +26 -0
  97. data/lib/rbx/object.fy +8 -3
  98. data/lib/rbx/regexp.fy +6 -3
  99. data/lib/rbx/string.fy +9 -1
  100. data/lib/rbx/stringio.fy +12 -0
  101. data/lib/rbx/symbol.fy +4 -0
  102. data/lib/stack.fy +1 -1
  103. data/lib/string.fy +34 -0
  104. data/lib/stringio.fy +1 -1
  105. data/lib/symbol.fy +6 -2
  106. data/lib/system.fy +15 -1
  107. data/lib/tuple.fy +5 -2
  108. data/lib/version.fy +1 -1
  109. data/ruby_lib/fdoc +2 -22
  110. data/tests/array.fy +3 -17
  111. data/tests/class.fy +312 -10
  112. data/tests/contracts.fy +51 -0
  113. data/tests/distributing_proxy.fy +28 -0
  114. data/tests/enumerable.fy +104 -1
  115. data/tests/exception.fy +35 -0
  116. data/tests/fixnum.fy +1 -1
  117. data/tests/hash.fy +81 -1
  118. data/tests/integer.fy +9 -0
  119. data/tests/matchers.fy +18 -0
  120. data/tests/method.fy +8 -14
  121. data/tests/object.fy +76 -2
  122. data/tests/option_parser.fy +80 -0
  123. data/tests/string.fy +21 -0
  124. data/tests/stringio.fy +1 -1
  125. data/tests/tuple.fy +1 -1
  126. metadata +21 -44
  127. data/examples/arithmetic.fy +0 -7
  128. data/examples/array.fy +0 -50
  129. data/examples/boolean.fy +0 -24
  130. data/examples/class.fy +0 -68
  131. data/examples/constant_access.fy +0 -15
  132. data/examples/default_args.fy +0 -20
  133. data/examples/define_methods.fy +0 -15
  134. data/examples/dynamic_output.fy +0 -15
  135. data/examples/empty_catch.fy +0 -4
  136. data/examples/exception.fy +0 -9
  137. data/examples/files.fy +0 -23
  138. data/examples/finally.fy +0 -5
  139. data/examples/future.fy +0 -30
  140. data/examples/future_composition.fy +0 -20
  141. data/examples/futures.fy +0 -9
  142. data/examples/game_of_life.fy +0 -148
  143. data/examples/html_generator.fy +0 -84
  144. data/examples/implicit_return.fy +0 -3
  145. data/examples/matchers.fy +0 -6
  146. data/examples/nested_try.fy +0 -9
  147. data/examples/numbers.fy +0 -12
  148. data/examples/rbx/and_or.fy +0 -7
  149. data/examples/rbx/blocks.fy +0 -22
  150. data/examples/rbx/classes.fy +0 -32
  151. data/examples/rbx/hello.fy +0 -8
  152. data/examples/rbx/include.fy +0 -12
  153. data/examples/rbx/inherit.fy +0 -11
  154. data/examples/rbx/methods.fy +0 -15
  155. data/examples/rbx/nested_classes.fy +0 -9
  156. data/examples/rbx/require.fy +0 -3
  157. data/examples/rbx/strings.fy +0 -5
  158. data/examples/require.fy +0 -7
  159. data/examples/return.fy +0 -13
  160. data/examples/singleton_methods.fy +0 -21
  161. data/examples/threads.fy +0 -18
  162. data/examples/tuple.fy +0 -8
  163. data/examples/webserver/webserver.fy +0 -15
  164. data/lib/proxy.fy +0 -86
  165. data/lib/thread_pool.fy +0 -102
@@ -1,7 +1,6 @@
1
1
  class Fancy AST {
2
2
  class Script : Node {
3
- read_slots: ['file, 'line, 'body]
4
-
3
+ read_slots: ('file, 'line, 'body)
5
4
  @@stack = []
6
5
 
7
6
  def push_script {
@@ -16,8 +15,7 @@ class Fancy AST {
16
15
  @@stack last()
17
16
  }
18
17
 
19
- def initialize: @line file: @file body: @body {
20
- }
18
+ def initialize: @line file: @file body: @body
21
19
 
22
20
  def bytecode: g {
23
21
  pos(g)
@@ -7,9 +7,6 @@ class Fancy AST {
7
7
 
8
8
  def bytecode: g {
9
9
  pos(g)
10
- g push_self()
11
- g send(@access, 0)
12
- g pop()
13
10
  bytecode(g)
14
11
  }
15
12
  }
@@ -6,9 +6,9 @@ class Fancy AST {
6
6
  It works...
7
7
  """
8
8
 
9
- def initialize: @line code: @code
9
+ def initialize: @line code: @code filename: @filename
10
10
  def parse_code {
11
- Fancy Parser parse_code: @code file: ("<<StringInterpolation: " ++ (@code inspect) ++ ">>") line: @line
11
+ Fancy Parser parse_code: @code file: @filename line: @line
12
12
  }
13
13
  def bytecode: g {
14
14
  parse_code body expressions first bytecode: g
@@ -1,12 +1,10 @@
1
1
  class Fancy AST {
2
2
  class Super : Node {
3
- def initialize: @line {
4
- }
3
+ def initialize: @line
5
4
  }
6
5
 
7
6
  class SuperSend : Node {
8
- def initialize: @line message: @name args: @args {
9
- }
7
+ def initialize: @line message: @name args: @args
10
8
 
11
9
  def bytecode: g {
12
10
  pos(g)
@@ -28,7 +28,7 @@ class Fancy AST {
28
28
  finally_ = g new_label()
29
29
  done = g new_label()
30
30
 
31
- g setup_unwind(handler, Rubinius AST RescueType)
31
+ g setup_unwind(handler, Rubinius AST EnsureType)
32
32
 
33
33
  # make a break available to use
34
34
  current_break = g break()
@@ -42,9 +42,11 @@ class Fancy AST {
42
42
  if: current_redo then: { g redo=(g new_label()) }
43
43
 
44
44
  @body bytecode(g)
45
+ retval = g new_stack_local()
46
+ g set_stack_local(retval)
47
+ g pop()
45
48
 
46
49
  g pop_unwind()
47
- g pop()
48
50
  g goto(finally_)
49
51
 
50
52
  if: current_break then: {
@@ -109,7 +111,7 @@ class Fancy AST {
109
111
 
110
112
  reraise set!()
111
113
 
112
- # execte the finally block before propagating the exception
114
+ # execute the finally block before propagating the exception
113
115
  @ensure bytecode: g
114
116
 
115
117
  # remove the exception so we have the state
@@ -120,6 +122,7 @@ class Fancy AST {
120
122
  g reraise()
121
123
 
122
124
  finally_ set!()
125
+
123
126
  @ensure bytecode: g
124
127
 
125
128
  done set!()
@@ -127,12 +130,15 @@ class Fancy AST {
127
130
  g push_stack_local(outer_ex)
128
131
  g restore_exception_state()
129
132
  g pop_modifiers()
133
+
134
+ g pop()
135
+
136
+ g push_stack_local(retval)
130
137
  }
131
138
  }
132
139
 
133
140
  class ExceptionHandler : Node {
134
- def initialize: @line condition: @condition var: @var body: @body {
135
- }
141
+ def initialize: @line condition: @condition var: @var body: @body
136
142
 
137
143
  def bytecode: g final_tag: final_tag {
138
144
  pos(g)
@@ -160,8 +166,7 @@ class Fancy AST {
160
166
  }
161
167
 
162
168
  class CurrentException : Node {
163
- def initialize: @line {
164
- }
169
+ def initialize: @line
165
170
 
166
171
  def bytecode: g {
167
172
  pos(g)
@@ -170,8 +175,7 @@ class Fancy AST {
170
175
  }
171
176
 
172
177
  class Retry : Node {
173
- def initialize: @line {
174
- }
178
+ def initialize: @line
175
179
 
176
180
  def bytecode: g {
177
181
  pos(g)
@@ -1,7 +1,6 @@
1
1
  class Fancy AST {
2
2
  class TupleLiteral : Node {
3
- def initialize: @line entries: @elements {
4
- }
3
+ def initialize: @line entries: @elements
5
4
 
6
5
  def bytecode: g {
7
6
  pos(g)
@@ -37,7 +37,7 @@ class Fancy {
37
37
  result = nil
38
38
  try {
39
39
  result = compiler run()
40
- } catch Exception => e {
40
+ } catch StandardError => e {
41
41
  compiler_error("Error trying to compile " ++ file, e)
42
42
  }
43
43
  result
@@ -63,7 +63,7 @@ class Fancy {
63
63
  compiler run()
64
64
  } catch Fancy Parser ParseError => e {
65
65
  e raise!
66
- } catch Exception => e {
66
+ } catch StandardError => e {
67
67
  compiler_error("Error trying to compile " ++ file, e)
68
68
  }
69
69
  }
@@ -1,11 +1,10 @@
1
1
  class Fancy Compiler Stages {
2
2
  class Stage : Rubinius Compiler Stage {
3
- initialize = |compiler, last| {
3
+ define_method('initialize) |compiler last| {
4
4
  sup = Rubinius Compiler Stage instance_method('initialize)
5
5
  sup bind(self) call(compiler, last)
6
6
  initialize: compiler last: last
7
7
  }
8
- define_method('initialize, &initialize)
9
8
 
10
9
  def self stage: name next: next (nil) {
11
10
  stage(name)
@@ -41,8 +40,7 @@ class Fancy Compiler Stages {
41
40
  compiler parser: self
42
41
  }
43
42
 
44
- def input: @code file: @filename line: @line (1) {
45
- }
43
+ def input: @code file: @filename line: @line (1);
46
44
 
47
45
  def run {
48
46
  ast = Fancy Parser parse_code: @code file: @filename line: @line
@@ -60,8 +58,7 @@ class Fancy Compiler Stages {
60
58
  compiler parser: self
61
59
  }
62
60
 
63
- def input: @filename line: @line (1) {
64
- }
61
+ def input: @filename line: @line (1);
65
62
 
66
63
  def run {
67
64
  ast = Fancy Parser parse_file: @filename line: @line
@@ -1,57 +1,89 @@
1
- # class Class {
2
- # class Contracts {
3
- # class ConditionBuilder : Proxy {
4
- # def initialize: block for: @method class: @class {
5
- # @conditions = <[]>
6
- # block call: [self]
7
- # insert_validations
8
- # }
9
-
10
- # def unknown_message: m with_params: p {
11
- # if: (p size > 0 ) then: {
12
- # @conditions[m to_s[[0,-2]]]: $ p first
13
- # }
14
- # }
15
-
16
- # def insert_validations {
17
- # name = @method name()
18
- # alias_name = "__#{name}__orig__"
19
- # conditions = @conditions
20
-
21
- # @class alias_method: alias_name for: name
22
- # @class define_method: name with: |p| {
23
- # conditions each: |attr block| {
24
- # # TODO
25
- # }
26
- # receive_message: alias_name with_params: p
27
- # }
28
- # }
29
- # }
30
- # }
31
-
32
- # def preconditions: block for: method {
33
- # Contracts ConditionBuilder new: block for: method class: self
34
- # }
35
-
36
- # def postconditions: block for: method {
37
- # Contracts ConditionBuilder new: block for: method class: self
38
- # }
39
- # }
40
-
41
- # # example
42
-
43
- # class Person {
44
- # read_slots: ('name, 'age)
45
-
46
- # preconditions: @{
47
- # name: @{ blank? not }
48
- # age: @{ > 0 }
49
- # } for: $ def initialize: @name age: @age
50
- # }
51
-
52
-
53
- # p = Person new: "chris" age: 25
54
- # p inspect println
55
-
56
-
57
- nil
1
+ class Class {
2
+ class Contracts {
3
+ class InterfaceNotImplementedError : ArgumentError {
4
+ """
5
+ Exception class that gets raised during @Class@ inclusion
6
+ whenever a class doesn't implement another class' required
7
+ interface.
8
+ """
9
+
10
+ read_slots: ('methods, 'interface, 'including_class)
11
+ def initialize: @methods interface: @interface including_class: @including_class {
12
+ initialize: \
13
+ "Methods #{@methods inspect} not implemented for interface: #{@interface name} required in class: #{@including_class name}"
14
+ }
15
+ }
16
+ }
17
+
18
+ lazy_slot: 'expected_interface_methods value: { [] }
19
+ lazy_slot: 'provided_interface_methods value: { [] }
20
+
21
+ def expects_interface_on_inclusion: methods {
22
+ """
23
+ @methods Collection of method signatures to expect on inclusion into another @Class@.
24
+
25
+ Declares a required interface (collection of method signatures) an including class has to provide.
26
+
27
+ Example:
28
+ class Enumerable {
29
+ expects_interface_on_inclusion: ['each:]
30
+ }
31
+ """
32
+
33
+ @expected_interface_methods = methods to_a
34
+ }
35
+
36
+ alias_method: 'expects_interface: for: 'expects_interface_on_inclusion:
37
+
38
+ def provides_interface: methods {
39
+ """
40
+ @methods Collection of method signatures this class explicitly declares to provide.
41
+
42
+ Example:
43
+ class MyCollection {
44
+ # you can skip this if you actually define each: before you include Fancy Enumerable.
45
+ provides_interface: ['each]
46
+ includes: Fancy Enumerable
47
+ }
48
+ """
49
+
50
+ provided_interface_methods append: (methods to_a)
51
+ }
52
+
53
+ def provides_interface?: methods {
54
+ """
55
+ @methods Collection of method signatures (an interface) to check for.
56
+ @return @true if all methods in @methods are provided by @self, @false otherwise.
57
+ """
58
+
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) }
61
+ }
62
+
63
+ def missing_methods_for_interface: methods {
64
+ """
65
+ @methods Collection of method signatures to check.
66
+ @return Collection of methods in @methods this class doesn't provide.
67
+ """
68
+
69
+ pim = Set new: $ provided_interface_methods map: @{ message_name to_s } + instance_methods
70
+ methods select: |m| { pim includes?: (m message_name to_s) . not }
71
+ }
72
+
73
+ def included: class {
74
+ """
75
+ @class @Class@ to be included in @self. Checks possible interface requirements.
76
+
77
+ Default include hook. Make sure to call this via `super included: class`.
78
+ """
79
+
80
+ class provides_interface?: expected_interface_methods
81
+ unless: (class provides_interface?: expected_interface_methods) do: {
82
+ not_implemented_methods = class missing_methods_for_interface: expected_interface_methods
83
+ if: (not_implemented_methods size > 0) then: {
84
+ Contracts InterfaceNotImplementedError new: not_implemented_methods interface: self including_class: class . raise!
85
+ }
86
+ }
87
+ true
88
+ }
89
+ }
@@ -7,7 +7,8 @@ class DynamicSlotObject : Fancy BasicObject {
7
7
  dso name: \"Chris\"
8
8
  dso age: 25
9
9
  dso country: \"Germany\"
10
- dso object # => Object with slots 'name, 'age and 'country defined
10
+
11
+ dso object # => Object with slots 'name, 'age and 'country defined
11
12
  """
12
13
 
13
14
  def initialize {
@@ -15,6 +16,10 @@ class DynamicSlotObject : Fancy BasicObject {
15
16
  }
16
17
 
17
18
  def object {
19
+ """
20
+ @return @Object@ with slots defined dynamically by sending messages to @self.
21
+ """
22
+
18
23
  @object metaclass read_write_slots: (@object slots)
19
24
  @object
20
25
  }
@@ -35,14 +40,23 @@ class DynamicKeyHash : Fancy BasicObject {
35
40
  dkh name: \"Chris\"
36
41
  dkh age: 25
37
42
  dkh country: \"Germany\"
38
- dkh hash # => <['name => \"Chris\", 'age => 25, 'country => \"Germany\"]>
43
+
44
+ dkh hash # => <['name => \"Chris\", 'age => 25, 'country => \"Germany\"]>
39
45
  """
40
46
 
41
47
  def initialize: @deep (false) {
48
+ """
49
+ @deep If @true, recursively sends @to_hash to any value passed as an argument to @self that is a @Block@ (dynamically creating nested @Hash@es).
50
+ """
51
+
42
52
  @hash = <[]>
43
53
  }
44
54
 
45
55
  def hash {
56
+ """
57
+ @return @Hash@ generated dynamically by sending messages to @self.
58
+ """
59
+
46
60
  @hash
47
61
  }
48
62
 
@@ -70,7 +84,7 @@ class DynamicValueArray : Fancy BasicObject {
70
84
  dva country: \"Germany\"
71
85
  dva something_else
72
86
 
73
- dva array # => [['name, \"Chris\"], ['age, 25], ['country, \"Germany\"], 'something_else]
87
+ dva array # => [['name, \"Chris\"], ['age, 25], ['country, \"Germany\"], 'something_else]
74
88
  """
75
89
 
76
90
  def initialize {
@@ -78,6 +92,10 @@ class DynamicValueArray : Fancy BasicObject {
78
92
  }
79
93
 
80
94
  def array {
95
+ """
96
+ @return @Array@ generated dynamically by sending messages to @self.
97
+ """
98
+
81
99
  @arr
82
100
  }
83
101
 
@@ -4,6 +4,8 @@ class Fancy {
4
4
  Mixin-Class with useful methods for collections that implement an @each:@ method.
5
5
  """
6
6
 
7
+ expects_interface: 'each:
8
+
7
9
  def at: index {
8
10
  """
9
11
  @index @Fixnum@ that is the 0-based index into @self.
@@ -14,10 +16,8 @@ class Fancy {
14
16
  \"foo\” at: 3 # => nil
15
17
  """
16
18
 
17
- i = 0
18
- each: |x| {
19
+ each_with_index: |x i| {
19
20
  { return x } if: $ i == index
20
- i = i + 1
21
21
  }
22
22
  return nil
23
23
  }
@@ -80,6 +80,47 @@ class Fancy {
80
80
  item
81
81
  }
82
82
 
83
+ def rest {
84
+ """
85
+ @return @Array@ of all but the first element in @self.
86
+ """
87
+
88
+ drop: 1
89
+ }
90
+
91
+ def first: amount {
92
+ """
93
+ @amount Amount of first elements to take from @self.
94
+ @return @Array@ of first @amount elements in @self.
95
+
96
+ Example:
97
+ (1,2,3,4) first: 2 # => [1,2]
98
+ """
99
+
100
+ i = 0
101
+ take_while: {
102
+ i = i + 1
103
+ i <= amount
104
+ }
105
+ }
106
+
107
+ def last: amount {
108
+ """
109
+ @amount Amount of last elements to take from @self.
110
+ @return @Array@ of last @amount elements in @self.
111
+
112
+ Example:
113
+ (1,2,3,4) last: 2 # => [3,4]
114
+ """
115
+
116
+ start_index = size - amount
117
+ i = 0
118
+ drop_while: {
119
+ i = i + 1
120
+ i <= start_index
121
+ }
122
+ }
123
+
83
124
  def includes?: item {
84
125
  """
85
126
  @item Item to check if it's included in @self.
@@ -123,6 +164,21 @@ class Fancy {
123
164
  s
124
165
  }
125
166
 
167
+ def join_by: block {
168
+ """
169
+ @block @Block@ to be called pair-wise to produce a single value.
170
+ @return Result of calling @block pairwise (similar to using @Fancy::Enumerable#reduce:into:@).
171
+
172
+ Works similar to @Fancy::Enumerable#inject:into:@ but uses first element as value injected.
173
+
174
+ Example:
175
+ (1,2,3) reduce_by: '+ # => same as: (2,3) inject: 1 into: '+
176
+ """
177
+
178
+ first, *rest = self
179
+ rest inject: first into: block
180
+ }
181
+
126
182
  def any?: condition {
127
183
  """
128
184
  @condition @Block@ (or @Callable) that is used to check if any element in @self yields true for it.
@@ -318,7 +374,7 @@ class Fancy {
318
374
  def take: amount {
319
375
  """
320
376
  @amount Amount of elements to take from @self.
321
- @return First @amount elements of @self in an @Array@.
377
+ @return @Array@ of first @amount elements in @self.
322
378
 
323
379
  Example:
324
380
  [1,2,3,4] take: 2 # => [1,2]
@@ -575,6 +631,8 @@ class Fancy {
575
631
  tmp = []
576
632
  enum = to_enum
577
633
 
634
+ { return groups } if: (size <= 0)
635
+
578
636
  loop: {
579
637
  size times: {
580
638
  tmp << (enum next)
@@ -664,5 +722,83 @@ class Fancy {
664
722
 
665
723
  join
666
724
  }
725
+
726
+ def sorted? {
727
+ """
728
+ @return @true if @self is sorted, @false otherwise.
729
+
730
+ Example:
731
+ (1,2,3) sorted? # => true
732
+ (2,1,3) sorted? # => false
733
+ \"abc\" sorted? # => true
734
+ \"bac\" sorted? # => false
735
+ """
736
+
737
+ last = nil
738
+ each: |x| {
739
+ if: last then: {
740
+ { return false } unless: $ last <= x
741
+ }
742
+ last = x
743
+ }
744
+ true
745
+ }
746
+
747
+ def split_at: index {
748
+ """
749
+ @index Index at which @self should be split.
750
+ @return @Array@ of 2 @Array@s of elements in self splitted at @index.
751
+
752
+ Example:
753
+ [1,2,3,4,5] split_at: 2 # => [[1,2], [3,4,5]]
754
+ """
755
+
756
+ [take: index, drop: index]
757
+ }
758
+
759
+ def split_with: predicate_block {
760
+ """
761
+ @predicate_block @Block@ to be used as a predicate on where to split in @self.
762
+ @return @Array@ of 2 @Array@s of elements split based on @predicate_block.
763
+
764
+ Example:
765
+ [1,2,3,4,5] split_with: @{ < 3 } # => [[1, 2], [3, 4, 5]]
766
+ """
767
+
768
+ [take_while: predicate_block, drop_while: predicate_block]
769
+ }
770
+
771
+ def grep: pattern {
772
+ """
773
+ @pattern Pattern to be filtered by (via @Object#===@)
774
+ @return Elements in @self for which @pattern matches.
775
+
776
+ Example:
777
+ \"hello world\" grep: /[a-h]/ # => [\"h\", \"e\", \"d\"]
778
+ [\"hello\", \"world\", 1, 2, 3] grep: String # => [\"hello\", \"world\"]
779
+ """
780
+
781
+ select: |x| { pattern === x }
782
+ }
783
+
784
+ def grep: pattern taking: block {
785
+ """
786
+ @pattern Pattern to be filtered by (via @Object#===@)
787
+ @block @Block@ to be called with each element for which @pattern matches.
788
+ @return Return values of elements in @self called with @block for which @pattern matches.
789
+
790
+ Example:
791
+ \"hello world\" grep: /[a-h]/ taking: @{ upcase } # => [\"H\", \"E\", \"D\"]
792
+ [\"hello\", \"world\", 1, 2, 3] grep: String taking: 'upcase # => [\"HELLO\", \"WORLD\"]
793
+ """
794
+
795
+ result = []
796
+ each: |x| {
797
+ match x {
798
+ case pattern -> result << (block call: [x])
799
+ }
800
+ }
801
+ result
802
+ }
667
803
  }
668
804
  }