butler 1.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 (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