fancy 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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,4 +1,9 @@
1
+ Directory = Dir
2
+
1
3
  class Directory {
4
+ forwards_unary_ruby_methods
5
+ metaclass forwards_unary_ruby_methods
6
+
2
7
  def self create: dirname {
3
8
  """
4
9
  @dirname Path of @Directory@ to create.
@@ -39,7 +44,7 @@ class Directory {
39
44
 
40
45
  try {
41
46
  Dir delete(dirname)
42
- } catch Exception => e {
47
+ } catch StandardError => e {
43
48
  IOError new: (e message) . raise!
44
49
  }
45
50
  }
@@ -49,8 +54,8 @@ class Directory {
49
54
  @pattern Directory pattern or name containing files to be returned as an @Array@.
50
55
  @return @Array@ of files matching directory @pattern.
51
56
 
52
- Example usage:
53
- Directory list: \"tests/**/*.fy\" # => [\"tests/file1.fy\", \"tests/more/file2.fy\"]
57
+ Example:
58
+ Directory list: \"tests/**/*.fy\" # => [\"tests/file1.fy\", \"tests/more/file2.fy\"]
54
59
  """
55
60
 
56
61
  match pattern {
@@ -6,7 +6,7 @@ class Fancy Documentation {
6
6
  @docs = [docstring]
7
7
  }
8
8
 
9
- def meta: @meta { }
9
+ def meta: @meta
10
10
  def meta { @meta }
11
11
 
12
12
  # A list of handlers that would like to get adviced when
@@ -126,6 +126,19 @@ class File {
126
126
  }
127
127
  }
128
128
 
129
+ def File delete!: filename {
130
+ """
131
+ @filename Path to @File@ to be deleted.
132
+
133
+ Deletes a @File@ with a given @filename. If an @IOError@ occurs,
134
+ it gets ignored.
135
+ """
136
+
137
+ try {
138
+ File delete: filename
139
+ } catch IOError {}
140
+ }
141
+
129
142
  def File directory?: path {
130
143
  """
131
144
  @path Path to check if it's a @Directory@.
@@ -148,6 +161,15 @@ class File {
148
161
  File rename(old_name, new_name)
149
162
  }
150
163
 
164
+ def File absolute_path: filename {
165
+ """
166
+ @filename Name of @File@ to get absolute path for.
167
+ @return Absolute (expanded) path for @filename.
168
+ """
169
+
170
+ File expand_path: filename
171
+ }
172
+
151
173
  def initialize: path {
152
174
  initialize(path)
153
175
  }
@@ -4,7 +4,7 @@ class Integer {
4
4
  def times: block {
5
5
  """
6
6
  @block @Block@ to be called with each number between 0 and @self.
7
- @return @self
7
+ @return @self.
8
8
 
9
9
  Calls a given @Block@ with each number between 0 and @self.
10
10
  """
@@ -4,7 +4,8 @@ class MatchData {
4
4
  (e.g. by using match/case expressions).
5
5
  """
6
6
 
7
- ruby_alias: 'size
7
+ forwards_unary_ruby_methods
8
+
8
9
  ruby_alias: '[]
9
10
 
10
11
  def at: idx {
@@ -75,6 +75,10 @@ class Method {
75
75
  ruby_alias: 'executable
76
76
  include: MethodMixin
77
77
  forwards_unary_ruby_methods
78
+
79
+ def call: args ([]) {
80
+ call(*args)
81
+ }
78
82
  }
79
83
 
80
84
  class UnboundMethod {
@@ -86,4 +90,26 @@ class UnboundMethod {
86
90
  ruby_alias: 'executable
87
91
  include: MethodMixin
88
92
  forwards_unary_ruby_methods
93
+
94
+ alias_method: 'bind: for_ruby: 'bind
95
+
96
+ def call: args ([]) {
97
+ call(*args)
98
+ }
99
+
100
+ def selector_with_args {
101
+ match name {
102
+ case ":[]" -> return "[arg_0]"
103
+ case "[]:" -> return "[arg_0]: arg_1"
104
+ }
105
+
106
+ match arity {
107
+ case 0 -> name rest
108
+ case _ ->
109
+ selectors = name split: ":"
110
+ (0..arity - 1) map: |i| {
111
+ "#{selectors[i]}: arg_#{i}"
112
+ } . join: " "
113
+ }
114
+ }
89
115
  }
@@ -1,5 +1,5 @@
1
1
  class Object {
2
- ruby_aliases: [ '==, '===, 'class, 'inspect, 'object_id, 'instance_variables ]
2
+ ruby_aliases: [ '==, '===, 'class, 'inspect, 'object_id, 'instance_variables, 'methods, 'instance_variable_get, 'instance_variable_set ]
3
3
 
4
4
  def initialize {
5
5
  initialize()
@@ -14,8 +14,13 @@ class Object {
14
14
  Fancy CodeLoader require: file_path
15
15
  }
16
16
 
17
- def dclone {
18
- "Returns a deep clone of self using Ruby's Marshal class."
17
+ def dup {
18
+ """
19
+ @return Copy (clone) of self.
20
+
21
+ Returns a deep clone of self using Ruby's Marshal class.
22
+ """
23
+
19
24
  Marshal load(Marshal dump(self))
20
25
  }
21
26
 
@@ -6,9 +6,8 @@ class Regexp {
6
6
  ruby_alias: 'inspect
7
7
  ruby_alias: 'to_s
8
8
 
9
- def === string {
10
- ruby: 'match args: [string]
11
- }
9
+ alias_method: 'match: for_ruby: 'match
10
+ alias_method: '=== for_ruby: 'match
12
11
 
13
12
  def i {
14
13
  Regexp new(source(), true)
@@ -29,4 +28,8 @@ class Regexp {
29
28
  def Regexp [string] {
30
29
  new(string)
31
30
  }
31
+
32
+ def call: args {
33
+ args first =~ self
34
+ }
32
35
  }
@@ -3,12 +3,13 @@ class String {
3
3
  # prepend a : to fancy version of ruby methods.
4
4
  ruby_aliases: [
5
5
  '==, 'upcase, 'downcase, '=~, 'to_i, 'to_f,
6
- 'chomp, 'inspect, 'to_sym, '<, '>
6
+ 'chomp, 'inspect, 'to_sym, '<, '>, '<=, '>=, '<=>
7
7
  ]
8
8
 
9
9
  alias_method: 'ruby_idx: for_ruby: '[]
10
10
  alias_method: '[]: for_ruby: '[]=
11
11
  alias_method: 'scan: for_ruby: 'scan
12
+ alias_method: 'to_i: for_ruby: 'to_i
12
13
  alias_method: 'uppercase for: 'upcase
13
14
  alias_method: 'lowercase for: 'downcase
14
15
 
@@ -166,4 +167,11 @@ class String {
166
167
  def message_name {
167
168
  self to_sym message_name to_s
168
169
  }
170
+
171
+ def to_fancy_message {
172
+ match self {
173
+ case /^:/ -> rest to_sym
174
+ case _ -> self
175
+ }
176
+ }
169
177
  }
@@ -12,6 +12,18 @@ class StringIO {
12
12
  }
13
13
 
14
14
  def each_line: block {
15
+ """
16
+ @block @Block@ to be called with each line in @self.
17
+ """
18
+
15
19
  each_line(&block)
16
20
  }
21
+
22
+ def string: str {
23
+ """
24
+ @str @String@ to set @string in @self to.
25
+ """
26
+
27
+ string=(str)
28
+ }
17
29
  }
@@ -32,4 +32,8 @@ class Symbol {
32
32
  case false -> ":" <<(symbol) to_sym
33
33
  }
34
34
  }
35
+
36
+ def to_fancy_message {
37
+ to_s to_fancy_message to_sym
38
+ }
35
39
  }
@@ -40,7 +40,7 @@ class Stack {
40
40
  Pops the top-of-stack element from the Stack and returns it.
41
41
  """
42
42
 
43
- @arr remove_at: (size - 1)
43
+ @arr remove_at: -1
44
44
  }
45
45
 
46
46
  def top {
@@ -198,4 +198,38 @@ class String {
198
198
 
199
199
  File expand_path: $ File dirname(self) + "/" + path
200
200
  }
201
+
202
+ def multiline? {
203
+ """
204
+ @return @true if @self is a multiline string, @false otherwise.
205
+
206
+ Example:
207
+ \"foo\nbar\" multiline? # => true
208
+ \"foo bar\" multiline? # => false
209
+ \"\" multiline? # => false
210
+ \"\n\n\n\" multiline? # => true
211
+ """
212
+
213
+ grep: /\n/ . size > 0
214
+ }
215
+
216
+ def main? {
217
+ """
218
+ @return @true if @self is the filename of the script that got executed initially.
219
+ """
220
+
221
+ File expand_path: (ARGV[0] to_s) == self
222
+ }
223
+
224
+ def if_main: main_block else: else_block ({}) {
225
+ """
226
+ @main_block @Block@ to be run if @String#:main?@ returns true.
227
+ @else_block @Block@ to be called otherwise.
228
+
229
+ Same as:
230
+ if: main? then: else_block else: else_block
231
+ """
232
+
233
+ if: main? then: main_block else: else_block
234
+ }
201
235
  }
@@ -1 +1 @@
1
- require: "rbx/stringio"
1
+ require: "rbx/stringio"
@@ -8,8 +8,11 @@ class Symbol {
8
8
 
9
9
  def call: arg {
10
10
  """
11
- This allows Symbols to be used like Blocks
12
- (e.g. in all methods of Enumerable).
11
+ @arg Argument to send @self to.
12
+ @return Value of sending @self as message to @arg.
13
+
14
+ This allows Symbols to be used like Blocks (e.g. in all methods of Enumerable).
15
+
13
16
  Example:
14
17
  [1, 2, 3] map: 'squared # => [1, 4, 9]
15
18
  """
@@ -24,6 +27,7 @@ class Symbol {
24
27
  def call {
25
28
  """
26
29
  Sends @self as message to the sender in its context.
30
+
27
31
  Example:
28
32
  'foo call
29
33
  # => same as
@@ -12,11 +12,25 @@ class System {
12
12
  """
13
13
  @message Error message to be printed before aborting programm execution.
14
14
 
15
- Prints a given message on @*stderr* and then exits with an exit
15
+ Prints @message on @\*stderr\* and exits with an exit code of 1 (indicating
16
+ failure).
17
+ """
18
+
19
+ *stderr* println: message
20
+ abort
21
+ }
22
+
23
+ def System aborting: message do: block {
24
+ """
25
+ @message Message to print on @*stderr* before calling @block and exiting.
26
+ @block @Block@ to be called before exiting execution.
27
+
28
+ Prints @message on @\*stderr\*, calls @block and finally exits with an exit
16
29
  code of 1 (indicating failure).
17
30
  """
18
31
 
19
32
  *stderr* println: message
33
+ block call
20
34
  abort
21
35
  }
22
36
  }
@@ -84,9 +84,12 @@ class Tuple {
84
84
  """
85
85
 
86
86
  size times: |i| {
87
- block call: [at: i]
87
+ try {
88
+ block call: [at: i]
89
+ } catch Fancy NextIteration {}
88
90
  }
89
- self
91
+
92
+ return self
90
93
  }
91
94
 
92
95
  def reverse_each: block {
@@ -1,6 +1,6 @@
1
1
  class Fancy {
2
2
  VERSION_MAJOR = 0
3
- VERSION_MINOR = 7
3
+ VERSION_MINOR = 8
4
4
  VERSION_PATCH = 0
5
5
 
6
6
  VERSION = [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH] join: "."
@@ -1,23 +1,3 @@
1
- #!/usr/bin/env rbx
2
- # -*- ruby -*-
1
+ #!/usr/bin/env fancy
3
2
 
4
- class Fancy; end
5
- class Fancy::Parser; end
6
-
7
- # fdoc is a documentation generator for fancy.
8
- # This is a ruby script because we need to setup a hook
9
- # BEFORE any fancy code is loaded. So we can create
10
- # documentation for Fancy's builtin objects as well.
11
- # See fdoc.
12
- base = File.expand_path("../boot", File.dirname(__FILE__))
13
- require File.expand_path("rbx-compiler/parser/fancy_parser", base)
14
- require File.expand_path("rbx-compiler/compiler", base)
15
- require File.expand_path("code_loader", base)
16
- require File.expand_path("fancy_ext", base)
17
-
18
- Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/rbx/documentation.fyc", base)
19
- Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/fdoc_hook.fyc", base)
20
-
21
- Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/rbx.fyc", base)
22
-
23
- Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/fdoc.fyc", base)
3
+ require: "../lib/fdoc"
@@ -122,6 +122,9 @@ FancySpec describe: Array with: {
122
122
  arr index: 3 . is: 3
123
123
  arr index: 4 . is: 4
124
124
  arr index: 'foo . is: nil
125
+
126
+ arr index: @{ is_a?: Symbol } . is: 2
127
+ arr index: @{ is_a?: String } . is: nil
125
128
  }
126
129
 
127
130
  it: "returns an Array of all its indices" with: 'indices when: {
@@ -395,15 +398,6 @@ FancySpec describe: Array with: {
395
398
  arr is: [3,2,1]
396
399
  }
397
400
 
398
- it: "takes elements from itself as long a block yields true" with: 'take_while: when: {
399
- 1 upto: 15 . take_while: |x| { x < 10 } . is: (1 upto: 9)
400
- }
401
-
402
-
403
- it: "drops elements from itself as long a block yields true" with: 'drop_while: when: {
404
- 1 upto: 15 . drop_while: |x| { x < 10 } . is: (10 upto: 15)
405
- }
406
-
407
401
  it: "partitions an array via a given block" with: 'partition_by: when: {
408
402
  arr = [1,2,2,3,3,3,4,4,4,4,5]
409
403
  arr partition_by: 'identity . is: [[1], [2,2], [3,3,3], [4,4,4,4], [5]]
@@ -547,14 +541,6 @@ FancySpec describe: Array with: {
547
541
  arr sort_by: 'second . is: sorted
548
542
  }
549
543
 
550
- it: "returns the array in groups of 3" with: 'in_groups_of: when: {
551
- ['a,'b,'c] in_groups_of: 1 . is: [['a],['b],['c]]
552
- array = 1 upto: 10
553
- array in_groups_of: 3 . is: [[1,2,3], [4,5,6], [7,8,9], [10]]
554
-
555
- (20,30,40) in_groups_of: 2 . is: [[20,30], [40]]
556
- }
557
-
558
544
  it: "returns a hash" with: 'to_hash when: {
559
545
  [] to_hash is: <[]>
560
546
  [[1,2],[3,4]] to_hash is: <[1 => 2, 3 => 4]>
@@ -244,10 +244,8 @@ FancySpec describe: Class with: {
244
244
  }
245
245
 
246
246
  it: "is a subclass of another Class" with: 'subclass?: when: {
247
- class Super {
248
- }
249
- class Sub : Super {
250
- }
247
+ class Super
248
+ class Sub : Super
251
249
 
252
250
  Super subclass?: Object . is: true
253
251
  Sub subclass?: Object . is: true
@@ -450,12 +448,9 @@ FancySpec describe: Class with: {
450
448
  }
451
449
 
452
450
  it: "has the correct list of ancestors" with: 'ancestors when: {
453
- class A {
454
- }
455
- class B : A {
456
- }
457
- class C : B {
458
- }
451
+ class A
452
+ class B : A
453
+ class C : B
459
454
 
460
455
  A ancestors is: [A, Object, Kernel]
461
456
  B ancestors is: [B, A, Object, Kernel]
@@ -478,6 +473,20 @@ FancySpec describe: Class with: {
478
473
  { x b } raises: NoMethodError
479
474
  AClassWithPrivateMethods instance_method: 'a . private? is: true
480
475
  AClassWithPrivateMethods instance_method: 'b . private? is: true
476
+
477
+ class AClassWithPrivateMethods {
478
+ private: {
479
+ def c {
480
+ "private c"
481
+ }
482
+ def d {
483
+ "private d"
484
+ }
485
+ }
486
+ }
487
+
488
+ AClassWithPrivateMethods instance_method: 'c . private? is: true
489
+ AClassWithPrivateMethods instance_method: 'd . private? is: true
481
490
  }
482
491
 
483
492
  it: "makes methods protected" with: 'protected: when: {
@@ -498,6 +507,25 @@ FancySpec describe: Class with: {
498
507
  AClassWithProtectedMethods instance_method: 'b . private? is: false
499
508
  AClassWithProtectedMethods instance_method: 'a . protected? is: true
500
509
  AClassWithProtectedMethods instance_method: 'b . protected? is: true
510
+
511
+ class AClassWithProtectedMethods {
512
+ protected: {
513
+ def c {
514
+ "in c"
515
+ }
516
+ def d {
517
+ "in d"
518
+ }
519
+ }
520
+ }
521
+
522
+ { x a } raises: NoMethodError
523
+ { x b } raises: NoMethodError
524
+ AClassWithProtectedMethods instance_method: 'c . private? is: false
525
+ AClassWithProtectedMethods instance_method: 'd . private? is: false
526
+ AClassWithProtectedMethods instance_method: 'c . protected? is: true
527
+ AClassWithProtectedMethods instance_method: 'd . protected? is: true
528
+
501
529
  }
502
530
 
503
531
  it: "makes methods public" with: 'public: when: {
@@ -521,6 +549,26 @@ FancySpec describe: Class with: {
521
549
  AClassWithPublicMethods instance_method: 'b . protected? is: false
522
550
  AClassWithPublicMethods instance_method: 'a . public? is: true
523
551
  AClassWithPublicMethods instance_method: 'b . public? is: true
552
+
553
+ class AClassWithPublicMethods {
554
+ public: {
555
+ def c {
556
+ "in c"
557
+ }
558
+ def d {
559
+ "in d"
560
+ }
561
+ }
562
+ }
563
+
564
+ { x c } does_not raise: NoMethodError
565
+ { x d } does_not raise: NoMethodError
566
+ AClassWithPublicMethods instance_method: 'c . private? is: false
567
+ AClassWithPublicMethods instance_method: 'd . private? is: false
568
+ AClassWithPublicMethods instance_method: 'c . protected? is: false
569
+ AClassWithPublicMethods instance_method: 'd . protected? is: false
570
+ AClassWithPublicMethods instance_method: 'c . public? is: true
571
+ AClassWithPublicMethods instance_method: 'd . public? is: true
524
572
  }
525
573
 
526
574
  it: "defines a class without a body" when: {
@@ -646,4 +694,258 @@ FancySpec describe: Class with: {
646
694
  NoMethods instance_methods is: $ Object instance_methods
647
695
  Set[OneMethod instance_methods] is: $ Set[Object instance_methods + (OneMethod instance_methods: false)]
648
696
  }
697
+
698
+ it: "defines a before_method handler" with: 'before_method:run: when: {
699
+ class BeforeMethodClass {
700
+ read_slot: 'x
701
+ def initialize: @x
702
+ def before: arr {
703
+ arr << "Before Method: #{@x}"
704
+ }
705
+ def my_method: arr {
706
+ arr << "My Method: #{@x}"
707
+ }
708
+
709
+ before_method: 'my_method: run: 'before:
710
+ }
711
+
712
+ b1 = BeforeMethodClass new: 1
713
+ b2 = BeforeMethodClass new: 2
714
+
715
+ array = []
716
+
717
+ b1 my_method: array
718
+ b2 my_method: array
719
+
720
+ array is: [
721
+ "Before Method: 1", "My Method: 1",
722
+ "Before Method: 2", "My Method: 2"
723
+ ]
724
+
725
+ # we can also pass blocks
726
+
727
+ BeforeMethodClass before_method: 'my_method: run: |receiver array| {
728
+ array << "Before Block: #{receiver x}"
729
+ }
730
+
731
+ array = []
732
+
733
+ b1 my_method: array
734
+ b2 my_method: array
735
+
736
+ array is: [
737
+ "Before Block: 1", "Before Method: 1", "My Method: 1",
738
+ "Before Block: 2", "Before Method: 2", "My Method: 2"
739
+ ]
740
+ }
741
+
742
+ it: "defines an after_method handler" with: 'after_method:run: when: {
743
+ class AfterMethodClass {
744
+ read_slot: 'x
745
+ def initialize: @x
746
+ def after: arr {
747
+ arr << "After Method: #{@x}"
748
+ }
749
+ def my_method: arr {
750
+ arr << "My Method: #{@x}"
751
+ }
752
+
753
+ after_method: 'my_method: run: 'after:
754
+ }
755
+
756
+ b1 = AfterMethodClass new: 1
757
+ b2 = AfterMethodClass new: 2
758
+
759
+ array = []
760
+
761
+ b1 my_method: array
762
+ b2 my_method: array
763
+
764
+ array is: [
765
+ "My Method: 1", "After Method: 1",
766
+ "My Method: 2", "After Method: 2"
767
+ ]
768
+
769
+ AfterMethodClass after_method: 'my_method: run: |receiver array| {
770
+ "block getting called yo"
771
+ array << "After Block: #{receiver x}"
772
+ }
773
+
774
+ array = []
775
+
776
+ b1 my_method: array
777
+ b2 my_method: array
778
+
779
+ array is: [
780
+ "My Method: 1", "After Method: 1", "After Block: 1",
781
+ "My Method: 2", "After Method: 2", "After Block: 2"
782
+ ]
783
+ }
784
+
785
+
786
+ it: "defines an around_method handler" with: 'around_method:run: when: {
787
+ class AroundMethodClass {
788
+ read_slot: 'x
789
+ def initialize: @x
790
+ def around: arr {
791
+ arr << "Around Method: #{@x}"
792
+ }
793
+ def my_method: arr {
794
+ arr << "My Method: #{@x}"
795
+ }
796
+
797
+ around_method: 'my_method: run: 'around:
798
+ }
799
+
800
+ b1 = AroundMethodClass new: 1
801
+ b2 = AroundMethodClass new: 2
802
+
803
+ array = []
804
+
805
+ b1 my_method: array
806
+ b2 my_method: array
807
+
808
+ array is: [
809
+ "Around Method: 1", "My Method: 1", "Around Method: 1",
810
+ "Around Method: 2", "My Method: 2", "Around Method: 2"
811
+ ]
812
+
813
+
814
+ AroundMethodClass around_method: 'my_method: run: |receiver array| {
815
+ array << "Around Block: #{receiver x}"
816
+ }
817
+
818
+ array = []
819
+
820
+ b1 my_method: array
821
+ b2 my_method: array
822
+
823
+ array is: [
824
+ "Around Block: 1", "Around Method: 1", "My Method: 1", "Around Method: 1", "Around Block: 1",
825
+ "Around Block: 2", "Around Method: 2", "My Method: 2", "Around Method: 2", "Around Block: 2"
826
+ ]
827
+ }
828
+
829
+ it: "defines a custom calling chain for a method" with: 'define_calling_chain:for_method: when: {
830
+ class CallingChainClass {
831
+ def foo: arr { arr << "foo" }
832
+ def bar: arr { arr << "bar" }
833
+ def baz: arr { arr << "baz" }
834
+
835
+ define_calling_chain: ('foo:, 'bar:, 'baz:) for_method: 'foo:
836
+ }
837
+
838
+ arr = []
839
+ CallingChainClass new foo: arr
840
+ arr is: ["foo", "bar", "baz"]
841
+
842
+ CallingChainClass define_calling_chain: [|receiver arr|{ receiver baz: arr }, 'bar:] for_method: 'bar:
843
+
844
+ arr = []
845
+ CallingChainClass new bar: arr
846
+ arr is: ["baz", "bar"]
847
+ }
848
+
849
+ it: "exposes a Fancy method as a Ruby method to Ruby" with: 'expose_to_ruby:as: when: {
850
+ class ExposeToRuby {
851
+ def my_method { "in my_method" }
852
+ expose_to_ruby: 'my_method
853
+ def == other { true }
854
+ expose_to_ruby: '== as: 'ruby_==
855
+ }
856
+
857
+ ExposeToRuby new tap: @{
858
+ my_method is: "in my_method"
859
+ send('my_method) is: "in my_method"
860
+ == 1 is: true
861
+ send('ruby_==, 1) is: true
862
+ }
863
+ }
864
+
865
+ it: "rebinds an instance method within a block" with: 'rebind_instance_method:with:within: when: {
866
+ class RebindInstanceMethod {
867
+ def foo: msg ("foo!") {
868
+ msg println
869
+ }
870
+ def bar: msg {
871
+ "bar: #{msg}" println
872
+ }
873
+ }
874
+
875
+ rim = RebindInstanceMethod new
876
+ s = StringIO new
877
+ let: '*stdout* be: s in: {
878
+ rim foo
879
+ rim foo: "hello!"
880
+ s string is: "foo!\nhello!\n"
881
+ s string: ""
882
+
883
+ RebindInstanceMethod rebind_instance_method: 'foo: with: |msg| {
884
+ msg * 2 println
885
+ } within: {
886
+ let: '*stdout* be: s in: {
887
+ rim foo: "hello!"
888
+ }
889
+ }
890
+ s string is: "hello!hello!\n"
891
+
892
+ s string: ""
893
+ rim foo: "hello!"
894
+ s string is: "hello!\n"
895
+
896
+ s string: ""
897
+ RebindInstanceMethod rebind_instance_method: 'foo: with: 'bar: within: {
898
+ rim foo: "Test"
899
+ }
900
+ s string is: "bar: Test\n"
901
+ }
902
+ }
903
+
904
+ it: "removes defined slot accessor methods" with: 'remove_slot_accessors_for: when: {
905
+ class SlotAccessors {
906
+ read_write_slots: ('rw1, 'rw2)
907
+ read_slots: ('r1, 'r2)
908
+ write_slots: ('w1, 'w2)
909
+ }
910
+
911
+ sa = SlotAccessors new
912
+ {
913
+ sa rw1 is: nil
914
+ sa rw2 is: nil
915
+ sa r1 is: nil
916
+ sa r2 is: nil
917
+
918
+ sa rw1: "rw1"
919
+ sa rw2: "rw2"
920
+ sa w1: "w1"
921
+ sa w2: "w2"
922
+
923
+ sa rw1 is: "rw1"
924
+ sa rw2 is: "rw2"
925
+ sa r1 is: nil
926
+ sa r2 is: nil
927
+ } does_not raise: NoMethodError
928
+
929
+ { sa w1 } raises: NoMethodError
930
+ { sa w2 } raises: NoMethodError
931
+
932
+ SlotAccessors remove_slot_accessors_for: ('rw1, 'r1, 'w1)
933
+
934
+ { sa rw1 } raises: NoMethodError
935
+ { sa rw1: "foo"} raises: NoMethodError
936
+
937
+ {
938
+ sa rw2
939
+ sa rw2: "foo"
940
+ } does_not raise: NoMethodError
941
+
942
+ { sa r1 } raises: NoMethodError
943
+ { sa r2 } does_not raise: NoMethodError
944
+
945
+ { sa w1 } raises: NoMethodError
946
+ { sa w1: "foo" } raises: NoMethodError
947
+
948
+ { sa w2 } raises: NoMethodError
949
+ { sa w2: "foo" } does_not raise: NoMethodError
950
+ }
649
951
  }