gamefic 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gamefic/action.rb +87 -56
  3. data/lib/gamefic/ansi.rb +55 -0
  4. data/lib/gamefic/character.rb +130 -76
  5. data/lib/gamefic/command.rb +19 -0
  6. data/lib/gamefic/core_ext/array.rb +51 -40
  7. data/lib/gamefic/core_ext/string.rb +4 -0
  8. data/lib/gamefic/describable.rb +108 -46
  9. data/lib/gamefic/direction.rb +46 -0
  10. data/lib/gamefic/director/delegate.rb +91 -0
  11. data/lib/gamefic/director/order.rb +10 -0
  12. data/lib/gamefic/director/parser.rb +119 -0
  13. data/lib/gamefic/director.rb +16 -197
  14. data/lib/gamefic/engine/cgi.rb +221 -0
  15. data/lib/gamefic/engine/tty.rb +237 -0
  16. data/lib/gamefic/engine.rb +88 -67
  17. data/lib/gamefic/entity.rb +96 -69
  18. data/lib/gamefic/grammar/conjugator.rb +20 -0
  19. data/lib/gamefic/grammar/gender.rb +11 -0
  20. data/lib/gamefic/grammar/person.rb +10 -0
  21. data/lib/gamefic/grammar/plural.rb +13 -0
  22. data/lib/gamefic/grammar/pronouns.rb +60 -0
  23. data/lib/gamefic/grammar/tense.rb +6 -0
  24. data/lib/gamefic/grammar/verb_set.rb +43 -0
  25. data/lib/gamefic/grammar/verbs.rb +25 -0
  26. data/lib/gamefic/grammar/word_adapter.rb +36 -0
  27. data/lib/gamefic/grammar.rb +13 -0
  28. data/lib/gamefic/html.rb +53 -0
  29. data/lib/gamefic/keywords.rb +51 -33
  30. data/lib/gamefic/node.rb +65 -58
  31. data/lib/gamefic/plot/article_mount.rb +22 -0
  32. data/lib/gamefic/plot/command_mount.rb +88 -0
  33. data/lib/gamefic/plot/entity_mount.rb +45 -0
  34. data/lib/gamefic/plot/query_mount.rb +9 -0
  35. data/lib/gamefic/plot/scene_mount.rb +181 -0
  36. data/lib/gamefic/plot/you_mount.rb +22 -0
  37. data/lib/gamefic/plot.rb +296 -247
  38. data/lib/gamefic/query/ambiguous_children.rb +5 -0
  39. data/lib/gamefic/query/base.rb +265 -0
  40. data/lib/gamefic/query/children.rb +10 -0
  41. data/lib/gamefic/query/expression.rb +47 -0
  42. data/lib/gamefic/query/family.rb +10 -0
  43. data/lib/gamefic/query/many_children.rb +7 -0
  44. data/lib/gamefic/query/matches.rb +11 -0
  45. data/lib/gamefic/query/parent.rb +10 -0
  46. data/lib/gamefic/query/plural_children.rb +14 -0
  47. data/lib/gamefic/query/self.rb +10 -0
  48. data/lib/gamefic/query/siblings.rb +10 -0
  49. data/lib/gamefic/query/text.rb +43 -0
  50. data/lib/gamefic/query.rb +19 -203
  51. data/lib/gamefic/rule.rb +18 -0
  52. data/lib/gamefic/scene/active.rb +25 -0
  53. data/lib/gamefic/scene/concluded.rb +22 -0
  54. data/lib/gamefic/scene/multiplechoice.rb +74 -0
  55. data/lib/gamefic/scene/paused.rb +26 -0
  56. data/lib/gamefic/scene/yesorno.rb +43 -0
  57. data/lib/gamefic/scene.rb +125 -0
  58. data/lib/gamefic/script/base.rb +33 -0
  59. data/lib/gamefic/script/file.rb +14 -0
  60. data/lib/gamefic/script/text.rb +14 -0
  61. data/lib/gamefic/script.rb +9 -0
  62. data/lib/gamefic/serialized.rb +24 -0
  63. data/lib/gamefic/shell.rb +9 -247
  64. data/lib/gamefic/snapshots.rb +134 -0
  65. data/lib/gamefic/source/base.rb +12 -0
  66. data/lib/gamefic/source/file.rb +23 -0
  67. data/lib/gamefic/source/text.rb +16 -0
  68. data/lib/gamefic/source.rb +9 -0
  69. data/lib/gamefic/stage.rb +75 -0
  70. data/lib/gamefic/syntax.rb +106 -124
  71. data/lib/gamefic/tester.rb +20 -0
  72. data/lib/gamefic/version.rb +3 -0
  73. data/lib/gamefic.rb +18 -12
  74. metadata +102 -70
  75. data/lib/gamefic/base.rb +0 -10
  76. data/lib/gamefic/before.rb +0 -12
  77. data/lib/gamefic/import/basics/actions/close.rb +0 -16
  78. data/lib/gamefic/import/basics/actions/commands.rb +0 -3
  79. data/lib/gamefic/import/basics/actions/drop-in.rb +0 -17
  80. data/lib/gamefic/import/basics/actions/drop-on.rb +0 -16
  81. data/lib/gamefic/import/basics/actions/drop.rb +0 -30
  82. data/lib/gamefic/import/basics/actions/enter.rb +0 -16
  83. data/lib/gamefic/import/basics/actions/go.rb +0 -35
  84. data/lib/gamefic/import/basics/actions/inventory.rb +0 -8
  85. data/lib/gamefic/import/basics/actions/leave.rb +0 -29
  86. data/lib/gamefic/import/basics/actions/look-in-at.rb +0 -27
  87. data/lib/gamefic/import/basics/actions/look-under.rb +0 -3
  88. data/lib/gamefic/import/basics/actions/look.rb +0 -71
  89. data/lib/gamefic/import/basics/actions/nil.rb +0 -25
  90. data/lib/gamefic/import/basics/actions/open.rb +0 -23
  91. data/lib/gamefic/import/basics/actions/quit.rb +0 -3
  92. data/lib/gamefic/import/basics/actions/take.rb +0 -107
  93. data/lib/gamefic/import/basics/entities/container.rb +0 -8
  94. data/lib/gamefic/import/basics/entities/entity.rb +0 -11
  95. data/lib/gamefic/import/basics/entities/fixture.rb +0 -5
  96. data/lib/gamefic/import/basics/entities/item.rb +0 -5
  97. data/lib/gamefic/import/basics/entities/portal.rb +0 -40
  98. data/lib/gamefic/import/basics/entities/room.rb +0 -30
  99. data/lib/gamefic/import/basics/entities/scenery.rb +0 -5
  100. data/lib/gamefic/import/basics/entities/supporter.rb +0 -6
  101. data/lib/gamefic/import/basics/entities/thing.rb +0 -16
  102. data/lib/gamefic/import/basics/queries/reachable.rb +0 -38
  103. data/lib/gamefic/import/basics/queries/room.rb +0 -8
  104. data/lib/gamefic/import/basics/queries/visible.rb +0 -32
  105. data/lib/gamefic/import/basics/rules/has-enough-light.rb +0 -14
  106. data/lib/gamefic/import/basics.old/actions/container.rb +0 -112
  107. data/lib/gamefic/import/basics.old/actions/inventory.rb +0 -50
  108. data/lib/gamefic/import/basics.old/actions/look.rb +0 -53
  109. data/lib/gamefic/import/basics.old/actions/meta.rb +0 -6
  110. data/lib/gamefic/import/basics.old/actions/traversal.rb +0 -35
  111. data/lib/gamefic/import/basics.old/actions.rb +0 -1
  112. data/lib/gamefic/import/basics.old/entities/container.rb +0 -3
  113. data/lib/gamefic/import/basics.old/entities/fixture.rb +0 -3
  114. data/lib/gamefic/import/basics.old/entities/item.rb +0 -3
  115. data/lib/gamefic/import/basics.old/entities/portal.rb +0 -43
  116. data/lib/gamefic/import/basics.old/entities/room.rb +0 -27
  117. data/lib/gamefic/import/basics.old/entities/scenery.rb +0 -3
  118. data/lib/gamefic/import/basics.old/entities/supporter.rb +0 -3
  119. data/lib/gamefic/import/basics.old/entities.rb +0 -1
  120. data/lib/gamefic/import/basics.old/room_modes.rb +0 -48
  121. data/lib/gamefic/import/basics.rb +0 -6
  122. data/lib/gamefic/import/room_modes.rb +0 -48
  123. data/lib/gamefic/import/standard.rb +0 -1
  124. data/lib/gamefic/meta.rb +0 -12
  125. data/lib/gamefic/optionset.rb +0 -114
  126. data/lib/gamefic/requirement.rb +0 -14
  127. data/lib/gamefic/story.rb +0 -14
  128. data/lib/gamefic/thing.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cdcd6a0fe04e0a8bbe946261ff8284fb4667e2f7
4
- data.tar.gz: 98bafd885d73b32653194ca36ad50285e2656d45
3
+ metadata.gz: f89e42b74949fc2f51e9e5b277ab1c2cf8278f62
4
+ data.tar.gz: 94b6aed64c5b1805c98fde960a125b9c1f716cda
5
5
  SHA512:
6
- metadata.gz: 5cef4896f4506c22cbc82dff02846805b64b8062ccc90ead1e9aeeac1df9e9cc50a2a79bffd0159da33896f497bb2ce4bdeec68a45c7bef715384846e2c9a842
7
- data.tar.gz: 2d2f4b0df8f65e54d1da9826e4e4ca1febf3b9a5858f200f81c4163afbbfd75502c8ceb187a4f7db102135683843bfe0dc3f2d6fd5bca258988f60e85a8e69ee
6
+ metadata.gz: 3279a5663b92106d222f2468d9a0add3c05fedeceedcc2077f050764d5c6c5077ae38c72a1bdd475ee04bb734c57046c1976311569626246a2299e6cd22249c5
7
+ data.tar.gz: 9f32e56986a439a517d7d5783a4f37cf05e8e6884caf60caff044015c40baf8754c830b5f29bb3c98fac430e0c69419cf9dd09bf4e96b80666b1c0599f4fe2bc
@@ -1,65 +1,96 @@
1
1
  module Gamefic
2
2
 
3
- class Action
4
- attr_reader :command, :order_key
5
- @@defaults = Array.new
3
+ # Actions manage the execution of commands that Characters can perform.
4
+ #
5
+ class Action
6
+ attr_reader :order_key, :queries
7
+ attr_writer :meta
6
8
  @@order_key_seed = 0
7
- def initialize(story, command, *queries, &proc)
8
- @plot = story
9
+
10
+ def initialize(plot, verb, *queries, &proc)
11
+ if !verb.kind_of?(Symbol)
12
+ verb = verb.to_s
13
+ verb = nil if verb == ''
14
+ end
15
+ @plot = plot
9
16
  @order_key = @@order_key_seed
10
17
  @@order_key_seed += 1
11
- if (command.kind_of?(Symbol) == false and !command.nil?)
12
- raise "Action commands must be symbols"
13
- end
14
- if (queries.length + 1 != proc.arity) and (queries.length == 0 and proc.arity != -1)
15
- raise "Number of contexts is not compatible with proc arguments"
16
- end
17
- @command = command
18
- @queries = queries
19
- @proc = proc
20
- if story == nil
21
- @@defaults.push self
22
- else
23
- story.send :add_action, self
24
- end
25
- end
26
- def self.defaults
27
- @@defaults.clone
28
- end
29
- def specificity
30
- spec = 0
31
- magnitude = 1
32
- @queries.each { |q|
33
- if q.kind_of?(Query::Base)
34
- spec += (q.specificity * magnitude)
35
- else
36
- spec += magnitude
37
- end
38
- #magnitude = magnitude * 10
39
- }
40
- return spec
41
- end
42
- def key
43
- @key
44
- end
45
- def queries
46
- @queries
47
- end
18
+ @proc = proc
19
+ if (verb.kind_of?(Symbol) == false and !verb.nil?)
20
+ raise "Action verbs must be symbols"
21
+ end
22
+ if !@proc.nil?
23
+ if (queries.length + 1 != @proc.arity) and (queries.length == 0 and @proc.arity != -1)
24
+ raise "Number of queries is not compatible with proc arguments"
25
+ end
26
+ end
27
+ @verb = verb
28
+ @queries = queries
29
+ if !plot.nil?
30
+ plot.send :add_action, self
31
+ end
32
+ end
33
+
34
+ # Get the specificity of the Action.
35
+ # Specificity indicates how narrowly the Action's queries filter matches.
36
+ # Actions with higher specificity are given higher priority when searching
37
+ # for the Action that matches a character command. For example, an Action
38
+ # with a Query that filters for a specific class of Entity has a higher
39
+ # specificity than an Action with a Query that accepts arbitrary text.
40
+ #
41
+ # @return [Fixnum]
42
+ def specificity
43
+ spec = 0
44
+ if verb.nil?
45
+ spec = -100
46
+ end
47
+ magnitude = 1
48
+ @queries.each { |q|
49
+ if q.kind_of?(Query::Base)
50
+ spec += (q.specificity * magnitude)
51
+ else
52
+ spec += magnitude
53
+ end
54
+ #magnitude = magnitude * 10
55
+ }
56
+ return spec
57
+ end
58
+
59
+ # Get the verb associated with this Action.
60
+ # The verb is represented by a Symbol in the imperative form, such as
61
+ # :take or :look_under.
62
+ #
63
+ # @return [Symbol] The Symbol representing the verb.
64
+ def verb
65
+ @verb
66
+ end
67
+
68
+ # Execute this Action. This method is typically called by the Plot when
69
+ # a Character performs a command.
48
70
  def execute *args
49
- @proc.call *args
71
+ @proc.call(*args)
72
+ end
73
+
74
+ def signature
75
+ sig = ["#{@verb}"]
76
+ @queries.each { |q|
77
+ sig.push q.signature
78
+ }
79
+ "#{sig.join(', ').gsub(/Gamefic::(Query::)?/, '')}(#{specificity})"
80
+ end
81
+
82
+ # Is this a meta Action?
83
+ # If an Action is flagged meta, it usually means that it provides
84
+ # information about the game or manages some aspect of the user interface.
85
+ # It shouldn't represent an Action that the player's character performs in
86
+ # the game world. Examples include Actions to display credits or
87
+ # instructions.
88
+ #
89
+ # @return [Boolean]
90
+ def meta?
91
+ @meta ||= false
50
92
  end
51
- private
52
- def self.explode(entity)
53
- arr = Array.new
54
- arr.push entity
55
- cls = entity.class
56
- while cls != Object
57
- arr.push cls
58
- cls = cls.superclass
59
- end
60
- arr.push String
61
- arr.push nil
62
- end
63
- end
93
+
94
+ end
64
95
 
65
96
  end
@@ -0,0 +1,55 @@
1
+ module Gamefic
2
+
3
+ # Constants for ANSI codes, plus Extras for custom formatting.
4
+ module Ansi
5
+ module Code
6
+ class Nonstandard < String
7
+
8
+ end
9
+ module Attribute
10
+ NORMAL = 0
11
+ BOLD = 1
12
+ UNDERSCORE = 4
13
+ BLINK = 5
14
+ REVERSE = 7
15
+ CONCEALED = 8
16
+ end
17
+ module Foreground
18
+ BLACK = 30
19
+ RED = 31
20
+ GREEN = 32
21
+ YELLOW = 33
22
+ BLUE = 34
23
+ MAGENTA = 35
24
+ CYAN = 36
25
+ WHITE = 37
26
+ end
27
+ module Background
28
+ BLACK = 40
29
+ RED = 41
30
+ GREEN = 42
31
+ YELLOW = 43
32
+ BLUE = 44
33
+ MAGENTA = 45
34
+ CYAN = 46
35
+ WHITE = 47
36
+ end
37
+ module Extra
38
+ BLOCK = Nonstandard.new("block")
39
+ HREF = Nonstandard.new("href")
40
+ IMAGE = Nonstandard.new("image")
41
+ SRC = Nonstandard.new("src")
42
+ UPPERCASE = Nonstandard.new("uppercase")
43
+ COMMAND = Nonstandard.new("command")
44
+ IGNORED = Nonstandard.new("ignored")
45
+ LINE = Nonstandard.new("line")
46
+ end
47
+ end
48
+ def self.graphics_mode(*settings)
49
+ ansi = settings.flatten.that_are_not(Code::Nonstandard)
50
+ return '' if ansi.length == 0
51
+ "\e[#{ansi.join(';')}m"
52
+ end
53
+ end
54
+
55
+ end
@@ -1,82 +1,136 @@
1
- module Gamefic
1
+ require 'gamefic/director'
2
2
 
3
- class Character < Thing
4
- attr_reader :state, :queue, :user, :last_command
5
- attr_accessor :object_of_pronoun
6
- def initialize(plot, args = {})
7
- @state = CharacterState.new(self)
8
- @queue = Array.new
3
+ module Gamefic
4
+ class Character < Entity
5
+ attr_reader :queue, :user
6
+ # @return [Gamefic::Director::Order]
7
+ attr_reader :last_order
8
+ # @return [Entity,nil]
9
+ attr_reader :last_object
10
+ attr_accessor :object_of_pronoun, :scene
11
+
12
+ def initialize(plot, args = {})
13
+ @queue = Array.new
14
+ super
15
+ @buffer_stack = 0
16
+ @buffer = ""
17
+ end
18
+
19
+ # Connect a User.
20
+ #
21
+ # @param user [User]
22
+ def connect(user)
23
+ @user = user
24
+ end
25
+
26
+ # Disconnect the current User.
27
+ #
28
+ def disconnect
29
+ # TODO: We might need some cleanup here. Like, move the character out of the game, or set a timeout to allow dropped users to reconnect... figure it out.
30
+ @user = nil
31
+ end
32
+
33
+ # Perform a command.
34
+ # The command can be specified as a String or a set of tokens. Either form
35
+ # should yield the same result, but using tokens can yield better
36
+ # performance since it doesn't need to parse the command first.
37
+ #
38
+ # The command will be executed immediately regardless of game state.
39
+ #
40
+ # @example Send a command as a string
41
+ # character.perform "take the key"
42
+ #
43
+ # @example Send a command as a set of tokens
44
+ # character.perform :take, @key
45
+ #
46
+ def perform(*command)
47
+ Director.dispatch(self, *command)
48
+ end
49
+
50
+ # Quietly perform a command.
51
+ # This method executes the command exactly as #perform does, except it
52
+ # buffers the resulting output instead of sending it to the user.
53
+ #
54
+ # @return [String] The output that resulted from performing the command.
55
+ def quietly(*command)
56
+ if @buffer_stack == 0
57
+ @buffer = ""
58
+ end
59
+ @buffer_stack += 1
60
+ self.perform *command
61
+ @buffer_stack -= 1
62
+ @buffer
63
+ end
64
+
65
+ # Send a message to the Character.
66
+ # This method will automatically wrap the message in HTML paragraphs.
67
+ # To send a message without paragraph formatting, use #stream instead.
68
+ #
69
+ # @param message [String]
70
+ def tell(message)
71
+ if user != nil and message.to_s != ''
72
+ if @buffer_stack > 0
73
+ @buffer += message
74
+ else
75
+ message = "<p>#{message}</p>"
76
+ # This method uses String#gsub instead of String#gsub! for
77
+ # compatibility with Opal.
78
+ message = message.gsub(/\n\n/, '</p><p>')
79
+ message = message.gsub(/\n/, '<br/>')
80
+ user.stream.send message
81
+ end
82
+ end
83
+ end
84
+
85
+ # Send a message to the Character as raw text.
86
+ # Unlike #tell, this method will not wrap the message in HTML paragraphs.
87
+ #
88
+ # @param message [String]
89
+ def stream(message)
90
+ user.stream.send message
91
+ end
92
+
93
+ def destroy
94
+ if @user != nil
95
+ @user.quit
96
+ end
9
97
  super
10
- end
11
- def connect(user)
12
- @user = user
13
- end
14
- def disconnect
15
- # TODO: We might need some cleanup here. Like, move the character out of the game, or set a timeout to allow dropped users to reconnect... figure it out.
16
- @user = nil
17
- end
18
- def perform(command)
19
- #if command != nil
20
- # @queue.push command
21
- #end
22
- @last_command = command
23
- if state.busy? == false
24
- Director.dispatch(self, command)
25
- else
26
- @queue.push command
27
- end
28
- end
29
- #def inject(command)
30
- # Director.dispatch(self, command)
31
- #end
32
- def tell(message, refresh = false)
33
- if user != nil and message.to_s != ''
34
- user.stream.send "#{message}\n"
35
- if (refresh == true)
36
- user.refresh
37
- end
38
- end
39
- end
40
- def state=(new_state)
41
- @state = new_state
42
- end
43
- def destroy
44
- if @user != nil
45
- @user.quit
46
- end
47
- super
48
- end
49
- def update
50
- super
51
- @state.update
52
- end
53
- end
54
- class CharacterState
55
- def initialize(character)
56
- @character = character
57
- post_initialize
58
- end
59
- def post_initialize
60
- # TODO: Required by subclasses?
61
- end
62
- def busy?
63
- false
64
- end
65
- def update
66
- while (line = @character.queue.shift)
67
- @character.perform line
68
- if @character.state != self
69
- break
70
- end
71
- end
72
- end
73
- def prompt
74
- "> "
75
98
  end
76
- end
77
- class GameOverState < CharacterState
78
- def prompt
79
- "GAME OVER"
99
+
100
+ # Proceed to the next Action in the current stack.
101
+ # This method is typically used in Action blocks to cascade through
102
+ # multiple implementations of the same verb.
103
+ #
104
+ # @example Proceed through two implementations of a verb
105
+ # introduction do |actor|
106
+ # actor[:has_eaten] = false # Initial value
107
+ # end
108
+ # respond :eat do |actor|
109
+ # actor[:has_eaten] = true
110
+ # end
111
+ # respond :eat do |actor|
112
+ # # This version will be executed first because it was implemented last
113
+ # actor.tell "You eat something."
114
+ # actor[:has_eaten] # Will be false on the first run
115
+ # actor.proceed # Execute the previous implementation
116
+ # actor[:has_eaten] #=> true
117
+ # end
118
+ #
119
+ def proceed
120
+ return if delegate_stack.last.nil?
121
+ delegate_stack.last.proceed
122
+ end
123
+
124
+ private
125
+ def delegate_stack
126
+ @delegate_stack ||= []
127
+ end
128
+ def last_order=(order)
129
+ return if order.nil?
130
+ @last_order = order
131
+ if !order.action.meta? and !order.arguments[0].nil? and !order.arguments[0][0].nil? and order.arguments[0][0].kind_of?(Entity)
132
+ @last_object = order.arguments[0][0]
133
+ end
80
134
  end
81
135
  end
82
136
 
@@ -0,0 +1,19 @@
1
+ module Gamefic
2
+ # A Command is a collection of tokens parsed from a Syntax.
3
+ # The Director uses Commands to find and execute corresponding Actions.
4
+ #
5
+ class Command
6
+ # @!attribute [r] verb
7
+ # @return [Symbol] A Symbol representing the command's verb or verbal phrase.
8
+ attr_reader :verb
9
+
10
+ # @!attribute [r] arguments
11
+ # @return [Array<String>] An Array of arguments to be mapped to an Action's Queries.
12
+ attr_reader :arguments
13
+
14
+ def initialize verb, arguments
15
+ @verb = verb
16
+ @arguments = arguments
17
+ end
18
+ end
19
+ end
@@ -1,47 +1,58 @@
1
1
  class Array
2
- def that_are(cls)
3
- if (cls.kind_of?(Class) or cls.kind_of?(Module))
4
- return self.clone.delete_if { |i| i.kind_of?(cls) == false }
2
+ # @return [Array]
3
+ def that_are(cls)
4
+ if (cls.kind_of?(Class) or cls.kind_of?(Module))
5
+ return self.clone.delete_if { |i| i.kind_of?(cls) == false }
5
6
  elsif cls.kind_of?(Symbol)
6
- return self.clone.delete_if { |i| i.option_selected?(cls) == false }
7
- else
8
- if self.include?(cls)
9
- return [cls]
10
- end
11
- return Array.new
12
- end
13
- end
14
- def that_are_not(cls)
15
- if (cls.kind_of?(Class) or cls.kind_of?(Module))
16
- return self.clone.delete_if { |i| i.kind_of?(cls) == true }
7
+ return self.clone.delete_if { |i| i.send(cls) == false }
8
+ else
9
+ if self.include?(cls)
10
+ return [cls]
11
+ end
12
+ return Array.new
13
+ end
14
+ end
15
+ # @return [Array]
16
+ def that_are_not(cls)
17
+ if (cls.kind_of?(Class) or cls.kind_of?(Module))
18
+ return self.clone.delete_if { |i| i.kind_of?(cls) == true }
17
19
  elsif cls.kind_of?(Symbol)
18
- return self.clone.delete_if { |i| i.option_selected?(cls) == true }
19
- else
20
- return self.clone - [cls]
21
- end
22
- end
23
- def random
24
- return self[rand(self.length)]
25
- end
20
+ return self.clone.delete_if { |i| i.send(cls) == true }
21
+ else
22
+ return self.clone - [cls]
23
+ end
24
+ end
25
+ def random
26
+ return self[rand(self.length)]
27
+ end
26
28
  def pop_random
27
29
  delete_at(rand(self.length))
28
30
  end
29
- def shuffle
30
- self.sort { |a, b|
31
- rand(3) <=> rand(3)
32
- }
33
- end
34
- def shuffle!
35
- self.sort! { |a, b|
36
- rand(3) <=> rand(3)
37
- }
38
- end
39
- def join_and(sep = ', ', andSep = ' and ', serial = true)
40
- if self.length < 3
41
- self.join(andSep)
42
- else
43
- start = self - [self.last]
44
- start.join(sep) + "#{serial ? sep.strip : ''}#{andSep}#{self.last}"
45
- end
46
- end
31
+ # @return [Array]
32
+ def shuffle
33
+ self.sort { |a, b|
34
+ rand(3) <=> rand(3)
35
+ }
36
+ end
37
+ # @return [Array]
38
+ def shuffle!
39
+ self.sort! { |a, b|
40
+ rand(3) <=> rand(3)
41
+ }
42
+ end
43
+ # Get a string representation of the array that separated elements with
44
+ # commas and adds a conjunction before the last element.
45
+ # @return [String]
46
+ def join_and(sep = ', ', andSep = ' and ', serial = true)
47
+ if self.length < 3
48
+ self.join(andSep)
49
+ else
50
+ start = self - [self.last]
51
+ start.join(sep) + "#{serial ? sep.strip : ''}#{andSep}#{self.last}"
52
+ end
53
+ end
54
+ # @return [String]
55
+ def join_or(sep = ', ', orSep = ' or ', serial = true)
56
+ join_and(sep, orSep, serial)
57
+ end
47
58
  end
@@ -1,10 +1,14 @@
1
1
  class String
2
+ # Capitalize the first letter without changing the rest of the string.
3
+ # (String#capitalize makes the rest of the string lower-case.)
2
4
  def capitalize_first
3
5
  "#{self[0,1].upcase}#{self[1,self.length]}"
4
6
  end
7
+ # @return [String]
5
8
  def cap_first
6
9
  self.capitalize_first
7
10
  end
11
+ # @return [Array]
8
12
  def split_words
9
13
  self.gsub(/ +/, ' ').strip.split
10
14
  end