butler 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. data/CHANGELOG +4 -0
  2. data/GPL.txt +340 -0
  3. data/LICENSE.txt +52 -0
  4. data/README +37 -0
  5. data/Rakefile +334 -0
  6. data/bin/botcontrol +230 -0
  7. data/data/butler/config_template.yaml +4 -0
  8. data/data/butler/dialogs/backup.rb +19 -0
  9. data/data/butler/dialogs/botcontrol.rb +4 -0
  10. data/data/butler/dialogs/config.rb +1 -0
  11. data/data/butler/dialogs/create.rb +53 -0
  12. data/data/butler/dialogs/delete.rb +3 -0
  13. data/data/butler/dialogs/en/backup.yaml +6 -0
  14. data/data/butler/dialogs/en/botcontrol.yaml +5 -0
  15. data/data/butler/dialogs/en/create.yaml +11 -0
  16. data/data/butler/dialogs/en/delete.yaml +2 -0
  17. data/data/butler/dialogs/en/help.yaml +17 -0
  18. data/data/butler/dialogs/en/info.yaml +13 -0
  19. data/data/butler/dialogs/en/list.yaml +4 -0
  20. data/data/butler/dialogs/en/notyetimplemented.yaml +2 -0
  21. data/data/butler/dialogs/en/rename.yaml +3 -0
  22. data/data/butler/dialogs/en/start.yaml +3 -0
  23. data/data/butler/dialogs/en/sync_plugins.yaml +3 -0
  24. data/data/butler/dialogs/en/uninstall.yaml +5 -0
  25. data/data/butler/dialogs/en/unknown_command.yaml +2 -0
  26. data/data/butler/dialogs/help.rb +11 -0
  27. data/data/butler/dialogs/info.rb +27 -0
  28. data/data/butler/dialogs/interactive.rb +1 -0
  29. data/data/butler/dialogs/list.rb +10 -0
  30. data/data/butler/dialogs/notyetimplemented.rb +1 -0
  31. data/data/butler/dialogs/rename.rb +4 -0
  32. data/data/butler/dialogs/selectbot.rb +2 -0
  33. data/data/butler/dialogs/start.rb +5 -0
  34. data/data/butler/dialogs/sync_plugins.rb +30 -0
  35. data/data/butler/dialogs/uninstall.rb +17 -0
  36. data/data/butler/dialogs/unknown_command.rb +1 -0
  37. data/data/butler/plugins/core/logout.rb +41 -0
  38. data/data/butler/plugins/core/plugins.rb +134 -0
  39. data/data/butler/plugins/core/privilege.rb +103 -0
  40. data/data/butler/plugins/core/user.rb +166 -0
  41. data/data/butler/plugins/dev/eval.rb +64 -0
  42. data/data/butler/plugins/dev/nometa.rb +14 -0
  43. data/data/butler/plugins/dev/onhandlers.rb +93 -0
  44. data/data/butler/plugins/dev/raw.rb +36 -0
  45. data/data/butler/plugins/dev/rawlog.rb +77 -0
  46. data/data/butler/plugins/games/eightball.rb +54 -0
  47. data/data/butler/plugins/games/mastermind.rb +174 -0
  48. data/data/butler/plugins/irc/action.rb +36 -0
  49. data/data/butler/plugins/irc/join.rb +38 -0
  50. data/data/butler/plugins/irc/notice.rb +36 -0
  51. data/data/butler/plugins/irc/part.rb +38 -0
  52. data/data/butler/plugins/irc/privmsg.rb +36 -0
  53. data/data/butler/plugins/irc/quit.rb +36 -0
  54. data/data/butler/plugins/operator/deop.rb +41 -0
  55. data/data/butler/plugins/operator/devoice.rb +41 -0
  56. data/data/butler/plugins/operator/limit.rb +47 -0
  57. data/data/butler/plugins/operator/op.rb +41 -0
  58. data/data/butler/plugins/operator/voice.rb +41 -0
  59. data/data/butler/plugins/public/help.rb +69 -0
  60. data/data/butler/plugins/public/login.rb +72 -0
  61. data/data/butler/plugins/public/usage.rb +49 -0
  62. data/data/butler/plugins/service/clones.rb +56 -0
  63. data/data/butler/plugins/service/define.rb +47 -0
  64. data/data/butler/plugins/service/log.rb +183 -0
  65. data/data/butler/plugins/service/svn.rb +91 -0
  66. data/data/butler/plugins/util/cycle.rb +98 -0
  67. data/data/butler/plugins/util/load.rb +41 -0
  68. data/data/butler/plugins/util/pong.rb +29 -0
  69. data/data/butler/strings/random/acknowledge.en.yaml +5 -0
  70. data/data/butler/strings/random/gratitude.en.yaml +3 -0
  71. data/data/butler/strings/random/hello.en.yaml +4 -0
  72. data/data/butler/strings/random/ignorance.en.yaml +7 -0
  73. data/data/butler/strings/random/ignorance_about.en.yaml +3 -0
  74. data/data/butler/strings/random/insult.en.yaml +3 -0
  75. data/data/butler/strings/random/rejection.en.yaml +12 -0
  76. data/data/man/botcontrol.1 +17 -0
  77. data/lib/access.rb +187 -0
  78. data/lib/access/admin.rb +16 -0
  79. data/lib/access/privilege.rb +122 -0
  80. data/lib/access/role.rb +102 -0
  81. data/lib/access/savable.rb +18 -0
  82. data/lib/access/user.rb +180 -0
  83. data/lib/access/yamlbase.rb +126 -0
  84. data/lib/butler.rb +188 -0
  85. data/lib/butler/bot.rb +247 -0
  86. data/lib/butler/control.rb +93 -0
  87. data/lib/butler/dialog.rb +64 -0
  88. data/lib/butler/initialvalues.rb +40 -0
  89. data/lib/butler/irc/channel.rb +135 -0
  90. data/lib/butler/irc/channels.rb +96 -0
  91. data/lib/butler/irc/client.rb +351 -0
  92. data/lib/butler/irc/hostmask.rb +53 -0
  93. data/lib/butler/irc/message.rb +184 -0
  94. data/lib/butler/irc/parser.rb +125 -0
  95. data/lib/butler/irc/parser/commands.rb +83 -0
  96. data/lib/butler/irc/parser/generic.rb +343 -0
  97. data/lib/butler/irc/socket.rb +378 -0
  98. data/lib/butler/irc/string.rb +186 -0
  99. data/lib/butler/irc/topic.rb +15 -0
  100. data/lib/butler/irc/user.rb +265 -0
  101. data/lib/butler/irc/users.rb +112 -0
  102. data/lib/butler/plugin.rb +249 -0
  103. data/lib/butler/plugin/configproxy.rb +35 -0
  104. data/lib/butler/plugin/mapper.rb +85 -0
  105. data/lib/butler/plugin/matcher.rb +55 -0
  106. data/lib/butler/plugin/onhandlers.rb +70 -0
  107. data/lib/butler/plugin/trigger.rb +58 -0
  108. data/lib/butler/plugins.rb +147 -0
  109. data/lib/butler/version.rb +17 -0
  110. data/lib/cloptions.rb +217 -0
  111. data/lib/cloptions/adapters.rb +24 -0
  112. data/lib/cloptions/switch.rb +132 -0
  113. data/lib/configuration.rb +223 -0
  114. data/lib/dialogline.rb +296 -0
  115. data/lib/dialogline/localizations.rb +24 -0
  116. data/lib/durations.rb +57 -0
  117. data/lib/event.rb +295 -0
  118. data/lib/event/at.rb +64 -0
  119. data/lib/event/every.rb +56 -0
  120. data/lib/event/timed.rb +112 -0
  121. data/lib/installer.rb +75 -0
  122. data/lib/iterator.rb +34 -0
  123. data/lib/log.rb +68 -0
  124. data/lib/log/comfort.rb +85 -0
  125. data/lib/log/converter.rb +23 -0
  126. data/lib/log/entry.rb +152 -0
  127. data/lib/log/fakeio.rb +55 -0
  128. data/lib/log/file.rb +54 -0
  129. data/lib/log/filereader.rb +81 -0
  130. data/lib/log/forward.rb +49 -0
  131. data/lib/log/methods.rb +39 -0
  132. data/lib/log/nolog.rb +18 -0
  133. data/lib/log/splitter.rb +26 -0
  134. data/lib/ostructfixed.rb +26 -0
  135. data/lib/ruby/array/columnize.rb +38 -0
  136. data/lib/ruby/dir/mktree.rb +28 -0
  137. data/lib/ruby/enumerable/join.rb +13 -0
  138. data/lib/ruby/exception/detailed.rb +24 -0
  139. data/lib/ruby/file/append.rb +11 -0
  140. data/lib/ruby/file/write.rb +11 -0
  141. data/lib/ruby/hash/zip.rb +15 -0
  142. data/lib/ruby/kernel/bench.rb +15 -0
  143. data/lib/ruby/kernel/daemonize.rb +42 -0
  144. data/lib/ruby/kernel/non_verbose.rb +17 -0
  145. data/lib/ruby/kernel/safe_fork.rb +18 -0
  146. data/lib/ruby/range/stepped.rb +11 -0
  147. data/lib/ruby/string/arguments.rb +72 -0
  148. data/lib/ruby/string/chunks.rb +15 -0
  149. data/lib/ruby/string/post_arguments.rb +44 -0
  150. data/lib/ruby/string/unescaped.rb +17 -0
  151. data/lib/scheduler.rb +164 -0
  152. data/lib/scriptfile.rb +101 -0
  153. data/lib/templater.rb +86 -0
  154. data/test/cloptions.rb +134 -0
  155. data/test/cv.rb +28 -0
  156. data/test/irc/client.rb +85 -0
  157. data/test/irc/client_login.txt +53 -0
  158. data/test/irc/client_subscribe.txt +8 -0
  159. data/test/irc/message.rb +30 -0
  160. data/test/irc/messages.txt +64 -0
  161. data/test/irc/parser.rb +13 -0
  162. data/test/irc/profile_parser.rb +12 -0
  163. data/test/irc/users.rb +28 -0
  164. metadata +256 -0
@@ -0,0 +1,18 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Log
10
+ # discards all messages
11
+ class NoLog
12
+ def respond_to?(m,*a)
13
+ true
14
+ end
15
+ def method_missing(*a)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module Log
2
+ class Splitter
3
+ def initialize(*receivers)
4
+ @receivers = receivers
5
+ end
6
+
7
+ def delete(receiver)
8
+ @receivers.delete(receiver)
9
+ end
10
+
11
+ def add(receiver)
12
+ @receivers << receiver
13
+ end
14
+
15
+ def method_missing(*a, &b)
16
+ @receivers.each { |r| r.__send__(*a, &b)
17
+ end
18
+
19
+ def inspect
20
+ "#<%s:%08x %s %s>" % [
21
+ self.class,
22
+ object_id,
23
+ @receivers.map { |r| r.inspect }.join(', ')
24
+ ]
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'ostruct'
10
+
11
+
12
+
13
+ class OpenStruct
14
+ def [](field)
15
+ @table[field.to_sym]
16
+ end
17
+
18
+ def []=(field,value)
19
+ new_ostruct_member(field)
20
+ @table[field.to_sym] = value
21
+ end
22
+
23
+ def to_hash
24
+ @table.dup
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ class Array
10
+ ColumnizeDefaults = {
11
+ :prepend => "",
12
+ :orientation => :vertical,
13
+ :spacing => 1
14
+ }
15
+
16
+ def columnize(columns=nil, opts={})
17
+ pre, ori, space = ColumnizeDefaults.merge(opts).values_at(:prepend, :orientation, :spacing)
18
+ columns ||= 2
19
+ numrows = length.quo(columns).ceil
20
+ sizes, print = Array.new(columns) { [] }, Array.new(numrows) { [] }
21
+
22
+ if ori == :vertical then
23
+ (self+Array.new(numrows*columns-length,"")).each_with_index { |e,i|
24
+ sizes[i.div(numrows)] << e
25
+ print[i%numrows] << e
26
+ }
27
+ else
28
+ (self+Array.new(numrows*columns-length,"")).each_with_index { |e,i|
29
+ sizes[i%columns] << e
30
+ print[i.div(columns)] << e
31
+ }
32
+ end
33
+
34
+ sizes = sizes.map { |e| e.map { |f| f.length }.max+space }
35
+ format = "#{pre}%-#{sizes.join('s %-')}s"
36
+ print.map { |e| format % e }.join("\n")
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ class <<Dir
10
+ # create directory structure
11
+ # Dir.mktree(
12
+ # "a" => {
13
+ # "b" => [
14
+ # "d",
15
+ # "e"
16
+ # ],
17
+ # },
18
+ # "in/directory"
19
+ # )
20
+ def mktree(tree, base=Dir.pwd)
21
+ dir = [];
22
+ tree.each { |dir, sub|
23
+ current = "#{base}/#{dir}"
24
+ mkdir(current)
25
+ mktree(sub, current) if sub
26
+ }
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Enumerable
10
+ def join(sep=$,)
11
+ to_a.join(sep)
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ class Exception
2
+ module Detailed
3
+ def self.extended(obj)
4
+ obj.initialize_details
5
+ end
6
+
7
+ def initialize_details
8
+ @prepend = []
9
+ @append = []
10
+ end
11
+
12
+ def prepend(string)
13
+ @prepend.unshift string
14
+ end
15
+
16
+ def append(string)
17
+ @append.unshift string
18
+ end
19
+
20
+ def to_s
21
+ (@prepend+[super]+@append).join(' ')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ def File.append(path, data)
10
+ File.open(path, "a") { |fh| fh.write(data) }
11
+ end
@@ -0,0 +1,11 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ def File.write(path, data)
10
+ File.open(path, "w") { |fh| fh.write(data) }
11
+ end
@@ -0,0 +1,15 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ # create a hash from an array with keys and an array with values
10
+ # you can set default or provide a block just as with Hash::new
11
+ def Hash.zip(keys, values, default=nil, &block)
12
+ hash = block_given? ? Hash.new(&block) : Hash.new(default)
13
+ keys.zip(values) { |k,v| hash[k]=v }
14
+ hash
15
+ end
@@ -0,0 +1,15 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Kernel
10
+ def bench(n=1,&b)
11
+ start = Time.now
12
+ n.to_i.times(&b)
13
+ Time.now - start
14
+ end
15
+ end
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'ruby/kernel/safe_fork'
10
+
11
+
12
+
13
+
14
+ module Kernel
15
+ # This method causes the current running process to become a daemon
16
+ # All further printing is relied to the error.log
17
+ # FIXME doesn't belong into Butler::Bot, rather into botcontrol
18
+ def daemonize(chdir=nil, &on_sighup)
19
+ srand # Split rand streams between spawning and daemonized process
20
+ safe_fork and exit # Fork and exit from the parent
21
+
22
+ # Detach from the controlling terminal
23
+ raise "Can't detach from controlling terminal" unless sess_id = Process.setsid
24
+
25
+ # Prevent the possibility of acquiring a controlling terminal
26
+ trap('SIGHUP', 'IGNORE')
27
+ exit if safe_fork
28
+
29
+ # In daemon mode, a SIGHUP means termination
30
+ trap('SIGHUP', &on_sighup)
31
+
32
+ # We can't get the originally controlling terminals stdout/stdin anymore
33
+ STDIN.reopen("/dev/null")
34
+ STDOUT.reopen("/dev/null", "a")
35
+ STDERR.reopen(STDOUT)
36
+
37
+ Dir.chdir(chdir) if chdir
38
+ File.umask 0033 # FIXME ask somebody knowledgable about a sensible value
39
+
40
+ sess_id
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Kernel
10
+ def non_verbose
11
+ old_verbose = $VERBOSE
12
+ $VERBOSE = false
13
+ yield
14
+ ensure
15
+ $VERBOSE = old_verbose
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Kernel
10
+ # Try to fork if at all possible retrying every +delay+ sec (5s default)
11
+ # if the maximum process limit for the system has been reached
12
+ def safe_fork(delay=5)
13
+ fork
14
+ rescue Errno::EWOULDBLOCK
15
+ sleep(delay)
16
+ retry
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'enumerator'
2
+
3
+ class Range
4
+ # Returns an array with all values starting from begin,
5
+ # in intervall size until end.
6
+ # Example:
7
+ # (2..17).stepped(4) # => [2, 6, 10, 14]
8
+ def stepped(size=1)
9
+ enum_for(:step, size).to_a
10
+ end
11
+ end
@@ -0,0 +1,72 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'ruby/string/unescaped'
10
+
11
+ class String
12
+ # the exception raised by String#arguments if an odd number of arguments is encountered.
13
+ # you can still extract the other parts of the parsing via its accessors.
14
+ class SingleQuoteException < RuntimeError
15
+ attr_reader :index
16
+ attr_reader :pre
17
+ attr_reader :post
18
+ def initialize(args, index)
19
+ @index, @pre, @post = index, args[0,index], args[index+1,args.length-index]
20
+ super("A single unescaped quote as argument #{index}")
21
+ end
22
+ end
23
+
24
+ # parse a string of single/double/unquoted arguments (or any mix of)
25
+ # raises an exception if an odd number of quotes is matched
26
+ # examples:
27
+ # 'hello hello "ruby world"'.arguments # => ["hello", "hello", "ruby world"]
28
+ # 'hello hello ruby\ world'.arguments # => ["hello", "hello", "ruby world"]
29
+ # 'hello hello ruby\ world'.arguments(false) # => ["hello", "hello", "ruby\\ world"]
30
+ def arguments(unescape=true)
31
+ args = scan(
32
+ /
33
+ # match a double quoted string
34
+ "(?:\\.|[^\\"])*" |
35
+
36
+ # match a single quoted string
37
+ '(?:\\.|[^\\'])*' |
38
+
39
+ # match non-quoted words (may contain escaped spaces)
40
+ (?:\\.|[^\\'"\s])+ |
41
+
42
+ # match a single " or '
43
+ ["']
44
+ /x
45
+ )
46
+ if unescape
47
+ args.map! { |arg| ((arg[0,1] == '"' && arg[1]) ? arg[1..-2] : arg).unescaped }
48
+ else
49
+ args.map! { |arg| ((arg[0,1] == '"' && arg[1]) ? arg[1..-2] : arg) }
50
+ end
51
+ if (sq = args.index('"')) || (sq = args.index("'")) then
52
+ raise SingleQuoteException.new(args, sq)
53
+ end
54
+ args
55
+ end
56
+ end
57
+
58
+ if __FILE__ == $0 then
59
+ require 'test/unit'
60
+ class TestStringArguments < Test::Unit::TestCase
61
+ def test_all
62
+ assert_equal(%w(), ''.arguments)
63
+ assert_equal(%w(hello world those are arguments), 'hello world those are arguments'.arguments)
64
+ assert_equal(%w(hello ruby\ world those are arguments), 'hello "ruby world" those are arguments'.arguments)
65
+ assert_equal(%w(hello ruby\ world), 'hello "ruby world"'.arguments)
66
+ assert_equal(%w(ruby\ world those are arguments), '"ruby world" those are arguments'.arguments)
67
+ assert_equal(%w(hello ruby\ world those are arguments), 'hello ruby\ world those are arguments'.arguments)
68
+ assert_raise(String::SingleQuoteException) { 'hello " ruby'.arguments }
69
+ assert_raise(String::SingleQuoteException) { "hello ' ruby".arguments }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,15 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ class String
10
+ # splits a string into chunks of length 'len'
11
+ def chunks(chunk_length)
12
+ scan(/.{1,#{chunk_length}}/)
13
+ end
14
+ end
15
+
@@ -0,0 +1,44 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'ruby/string/arguments'
10
+
11
+ class String
12
+ def post_arguments(unescaped=true)
13
+ post = []
14
+ offset = -1
15
+ found = 0
16
+ arguments = begin
17
+ arguments(false)
18
+ rescue SingleQuoteException => ex
19
+ ex.pre+[ex.post.join(" ")]
20
+ end
21
+ arguments.each { |argument|
22
+ post << (unescaped ? self[offset+1..-1].unescaped : self[offset+1..-1])
23
+ found = index(argument, offset+1)
24
+ offset = found+argument.length
25
+ }
26
+ post
27
+ end
28
+ end
29
+
30
+ if __FILE__ == $0 then
31
+ require 'test/unit'
32
+ class TestStringArguments < Test::Unit::TestCase
33
+ def test_all
34
+ #assert_equal(%w(), ''.arguments)
35
+ #assert_equal(%w(hello world those are arguments), 'hello world those are arguments'.arguments)
36
+ #assert_equal(%w(hello ruby\ world those are arguments), 'hello "ruby world" those are arguments'.arguments)
37
+ #assert_equal(%w(hello ruby\ world), 'hello "ruby world"'.arguments)
38
+ #assert_equal(%w(ruby\ world those are arguments), '"ruby world" those are arguments'.arguments)
39
+ #assert_equal(%w(hello ruby\ world those are arguments), 'hello ruby\ world those are arguments'.arguments)
40
+ #assert_raise(String::SingleQuoteException) { 'hello " ruby'.arguments }
41
+ #assert_raise(String::SingleQuoteException) { "hello ' ruby".arguments }
42
+ end
43
+ end
44
+ end