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,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
  }