gamefic 0.2.0 → 0.6.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 (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