user-choices 1.1.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 (41) hide show
  1. data/History.txt +17 -0
  2. data/LICENSE.txt +34 -0
  3. data/Manifest.txt +40 -0
  4. data/README.txt +1 -0
  5. data/Rakefile +19 -0
  6. data/Rakefile.hoe +24 -0
  7. data/examples/older/README.txt +133 -0
  8. data/examples/older/command-line.rb +51 -0
  9. data/examples/older/default-values.rb +47 -0
  10. data/examples/older/multiple-sources.rb +63 -0
  11. data/examples/older/postprocess.rb +45 -0
  12. data/examples/older/switches.rb +50 -0
  13. data/examples/older/two-args.rb +37 -0
  14. data/examples/older/types.rb +67 -0
  15. data/examples/tutorial/index.html +648 -0
  16. data/examples/tutorial/tutorial1.rb +48 -0
  17. data/examples/tutorial/tutorial2.rb +52 -0
  18. data/examples/tutorial/tutorial3.rb +55 -0
  19. data/examples/tutorial/tutorial4.rb +55 -0
  20. data/examples/tutorial/tutorial5.rb +42 -0
  21. data/examples/tutorial/tutorial6.rb +42 -0
  22. data/examples/tutorial/tutorial7.rb +48 -0
  23. data/lib/user-choices/arglist-strategies.rb +178 -0
  24. data/lib/user-choices/builder.rb +89 -0
  25. data/lib/user-choices/command-line-source.rb +220 -0
  26. data/lib/user-choices/command.rb +42 -0
  27. data/lib/user-choices/conversions.rb +154 -0
  28. data/lib/user-choices/ruby-extensions.rb +20 -0
  29. data/lib/user-choices/sources.rb +269 -0
  30. data/lib/user-choices/version.rb +3 -0
  31. data/lib/user-choices.rb +131 -0
  32. data/setup.rb +1585 -0
  33. data/test/arglist-strategy-tests.rb +42 -0
  34. data/test/builder-tests.rb +569 -0
  35. data/test/command-line-source-tests.rb +443 -0
  36. data/test/conversion-tests.rb +157 -0
  37. data/test/set-standalone-test-paths.rb +5 -0
  38. data/test/source-tests.rb +442 -0
  39. data/test/user-choices-slowtests.rb +274 -0
  40. data/user-choices.tmproj +575 -0
  41. metadata +138 -0
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # See the tutorial for explanations.
7
+
8
+ ### The following adjusts the load path so that the correct version of
9
+ ### a self-contained package is found, no matter where the script is
10
+ ### run from.
11
+ require 'pathname'
12
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
13
+ require 's4t-utils/load-path-auto-adjuster'
14
+
15
+ require 'pp'
16
+ require 'user-choices'
17
+
18
+ # See the tutorial for explanations.
19
+
20
+ class TutorialExample < UserChoices::Command
21
+ include UserChoices
22
+
23
+ def add_sources(builder)
24
+ builder.add_source(CommandLineSource, :usage,
25
+ "Usage: ruby #{$0} [options]")
26
+ builder.add_source(EnvironmentSource, :with_prefix, "myprog_")
27
+ builder.add_source(YamlConfigFileSource, :from_file, ".myprog-config.yml")
28
+ end
29
+
30
+ def add_choices(builder)
31
+ builder.add_choice(:connections, :type=>:integer, :default=>0) { | command_line |
32
+ command_line.uses_option("-c", "--connections COUNT",
33
+ "Number of connections to open.")
34
+ }
35
+ end
36
+
37
+ def execute
38
+ puts "There are #{@user_choices[:connections]} connections."
39
+ pp @user_choices
40
+ end
41
+ end
42
+
43
+
44
+ if $0 == __FILE__
45
+ S4tUtils.with_pleasant_exceptions do
46
+ TutorialExample.new.execute
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ # See the tutorial for explanations.
8
+
9
+ ### The following adjusts the load path so that the correct version of
10
+ ### a self-contained package is found, no matter where the script is
11
+ ### run from.
12
+ require 'pathname'
13
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
14
+ require 's4t-utils/load-path-auto-adjuster'
15
+
16
+ require 'pp'
17
+ require 'user-choices'
18
+
19
+ class TutorialExample < UserChoices::Command
20
+ include UserChoices
21
+
22
+ def add_sources(builder)
23
+ builder.add_source(CommandLineSource, :usage,
24
+ "Usage: ruby #{$0} [options] file1 [file2]")
25
+ builder.add_source(EnvironmentSource, :with_prefix, "myprog_")
26
+ builder.add_source(YamlConfigFileSource, :from_file, ".myprog-config.yml")
27
+ end
28
+
29
+ def add_choices(builder)
30
+ builder.add_choice(:connections, :type=>:integer, :default=>0) { | command_line |
31
+ command_line.uses_option("-c", "--connections COUNT",
32
+ "Number of connections to open.")
33
+ }
34
+ builder.add_choice(:ssh, :type=>:boolean, :default=>false) { | command_line |
35
+ command_line.uses_switch("-s", "--ssh",
36
+ "Use ssh to open connection.")
37
+ }
38
+ end
39
+
40
+ def execute
41
+ puts format("SSH %s be used.", @user_choices[:ssh] ? "should" : "should not")
42
+ puts "There are #{@user_choices[:connections]} connections."
43
+ pp @user_choices
44
+ end
45
+ end
46
+
47
+
48
+ if $0 == __FILE__
49
+ S4tUtils.with_pleasant_exceptions do
50
+ TutorialExample.new.execute
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ # See the tutorial for explanations.
8
+
9
+ ### The following adjusts the load path so that the correct version of
10
+ ### a self-contained package is found, no matter where the script is
11
+ ### run from.
12
+ require 'pathname'
13
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
14
+ require 's4t-utils/load-path-auto-adjuster'
15
+
16
+ require 'pp'
17
+ require 'user-choices'
18
+
19
+ class TutorialExample < UserChoices::Command
20
+ include UserChoices
21
+
22
+ def add_sources(builder)
23
+ builder.add_source(CommandLineSource, :usage,
24
+ "Usage: ruby #{$0} [options] file1 [file2]")
25
+ builder.add_source(EnvironmentSource, :with_prefix, "myprog_")
26
+ builder.add_source(YamlConfigFileSource, :from_file, ".myprog-config.yml")
27
+ end
28
+
29
+ def add_choices(builder)
30
+ builder.add_choice(:connections, :type=>:integer, :default=>0) { | command_line |
31
+ command_line.uses_option("-c", "--connections COUNT",
32
+ "Number of connections to open.")
33
+ }
34
+ builder.add_choice(:ssh, :type=>:boolean, :default=>false) { | command_line |
35
+ command_line.uses_switch("-s", "--ssh",
36
+ "Use ssh to open connection.")
37
+ }
38
+ builder.add_choice(:files) { | command_line |
39
+ command_line.uses_arglist
40
+ }
41
+ end
42
+
43
+ def execute
44
+ puts format("SSH %s be used.", @user_choices[:ssh] ? "should" : "should not")
45
+ puts "There are #{@user_choices[:connections]} connections."
46
+ pp @user_choices
47
+ end
48
+ end
49
+
50
+
51
+ if $0 == __FILE__
52
+ S4tUtils.with_pleasant_exceptions do
53
+ TutorialExample.new.execute
54
+ end
55
+ end
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+
7
+ # See the tutorial for explanations.
8
+
9
+ ### The following adjusts the load path so that the correct version of
10
+ ### a self-contained package is found, no matter where the script is
11
+ ### run from.
12
+ require 'pathname'
13
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
14
+ require 's4t-utils/load-path-auto-adjuster'
15
+
16
+ require 'pp'
17
+ require 'user-choices'
18
+
19
+ class TutorialExample < UserChoices::Command
20
+ include UserChoices
21
+
22
+ def add_sources(builder)
23
+ builder.add_source(CommandLineSource, :usage,
24
+ "Usage: ruby #{$0} [options] file1 [file2]")
25
+ builder.add_source(EnvironmentSource, :with_prefix, "myprog_")
26
+ builder.add_source(YamlConfigFileSource, :from_file, ".myprog-config.yml")
27
+ end
28
+
29
+ def add_choices(builder)
30
+ builder.add_choice(:connections, :type=>:integer, :default=>0) { | command_line |
31
+ command_line.uses_option("-c", "--connections COUNT",
32
+ "Number of connections to open.")
33
+ }
34
+ builder.add_choice(:ssh, :type=>:boolean, :default=>false) { | command_line |
35
+ command_line.uses_switch("-s", "--ssh",
36
+ "Use ssh to open connection.")
37
+ }
38
+ builder.add_choice(:files, :length => 1..2) { | command_line |
39
+ command_line.uses_arglist
40
+ }
41
+ end
42
+
43
+ def execute
44
+ puts format("SSH %s be used.", @user_choices[:ssh] ? "should" : "should not")
45
+ puts "There are #{@user_choices[:connections]} connections."
46
+ pp @user_choices
47
+ end
48
+ end
49
+
50
+
51
+ if $0 == __FILE__
52
+ S4tUtils.with_pleasant_exceptions do
53
+ TutorialExample.new.execute
54
+ end
55
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # See the tutorial for explanations.
7
+
8
+ ### The following adjusts the load path so that the correct version of
9
+ ### a self-contained package is found, no matter where the script is
10
+ ### run from.
11
+ require 'pathname'
12
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
13
+ require 's4t-utils/load-path-auto-adjuster'
14
+
15
+ require 'pp'
16
+ require 'user-choices'
17
+
18
+ class TutorialExample < UserChoices::Command
19
+ include UserChoices
20
+
21
+ def add_sources(builder)
22
+ builder.add_source(CommandLineSource, :usage,
23
+ "Usage: ruby #{$0} infile")
24
+ end
25
+
26
+ def add_choices(builder)
27
+ builder.add_choice(:infile) { | command_line |
28
+ command_line.uses_arg
29
+ }
30
+ end
31
+
32
+ def execute
33
+ pp @user_choices
34
+ end
35
+ end
36
+
37
+
38
+ if $0 == __FILE__
39
+ S4tUtils.with_pleasant_exceptions do
40
+ TutorialExample.new.execute
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # See the tutorial for explanations.
7
+
8
+ ### The following adjusts the load path so that the correct version of
9
+ ### a self-contained package is found, no matter where the script is
10
+ ### run from.
11
+ require 'pathname'
12
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
13
+ require 's4t-utils/load-path-auto-adjuster'
14
+
15
+ require 'pp'
16
+ require 'user-choices'
17
+
18
+ class TutorialExample < UserChoices::Command
19
+ include UserChoices
20
+
21
+ def add_sources(builder)
22
+ builder.add_source(CommandLineSource, :usage,
23
+ "Usage: ruby #{$0} infile")
24
+ end
25
+
26
+ def add_choices(builder)
27
+ builder.add_choice(:infile) { | command_line |
28
+ command_line.uses_optional_arg
29
+ }
30
+ end
31
+
32
+ def execute
33
+ pp @user_choices
34
+ end
35
+ end
36
+
37
+
38
+ if $0 == __FILE__
39
+ S4tUtils.with_pleasant_exceptions do
40
+ TutorialExample.new.execute
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-09.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # See the tutorial for explanations.
7
+
8
+ ### The following adjusts the load path so that the correct version of
9
+ ### a self-contained package is found, no matter where the script is
10
+ ### run from.
11
+ require 'pathname'
12
+ $:.unshift((Pathname.new(__FILE__).parent.parent.parent + 'lib').to_s)
13
+ require 's4t-utils/load-path-auto-adjuster'
14
+
15
+ require 'pp'
16
+ require 'user-choices'
17
+
18
+ class TutorialExample < UserChoices::Command
19
+ include UserChoices
20
+
21
+ def add_sources(builder)
22
+ builder.add_source(CommandLineSource, :usage,
23
+ "Usage: ruby #{$0} infile outfile")
24
+ end
25
+
26
+ def add_choices(builder)
27
+ builder.add_choice(:files, :length => 2) { | command_line |
28
+ command_line.uses_arglist
29
+ }
30
+ end
31
+
32
+ def postprocess_user_choices
33
+ @user_choices[:infile] = @user_choices[:files][0]
34
+ @user_choices[:outfile] = @user_choices[:files][1]
35
+ end
36
+
37
+
38
+ def execute
39
+ pp @user_choices
40
+ end
41
+ end
42
+
43
+
44
+ if $0 == __FILE__
45
+ S4tUtils.with_pleasant_exceptions do
46
+ TutorialExample.new.execute
47
+ end
48
+ end
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-08-10.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'user-choices/ruby-extensions'
7
+
8
+ module UserChoices # :nodoc:
9
+
10
+ # Arglists cause complications, mainly because a command's arglist is
11
+ # never optional. If you ever want it to be ignored, for example, you have to treat it
12
+ # specially. An AbstractArglistStrategy is a sequence of messages that can
13
+ # cope with those sort of complications. These messages are called at the
14
+ # appropriate time by a CommandLineSource.
15
+ #
16
+ # * <b>AbstractArglistStrategy#fill</b> takes the arglist and converts it to
17
+ # the value of some choice symbol. The name should remind you of AbstractSource#fill.
18
+ # * There may be conversions that make sense for values (for this choice symbol) when
19
+ # those values do <i>not</i> come from an arglist, but not when they do.
20
+ # <b>AbstractArglistStrategy#claim_conversions</b> squirrels them away to protect
21
+ # them from more generic processing. They are then specially processed by
22
+ # AbstractArglistStrategy#apply_claimed_conversions.
23
+ # * After conversions, there may still be work to do. There may be some special
24
+ # reconciling required to the entire collection of choices. (The final result
25
+ # may depend on what value the arglist provided and what value some other source
26
+ # provided.) <b>AbstractArglistStrategy#adjust</b> does that work.
27
+ class AbstractArglistStrategy # :nodoc:
28
+
29
+ attr_reader :choice
30
+
31
+ # A strategy applies an argument list named _choice_ that is a key
32
+ # in the <i>value_holder</i>. It's hackish, but don't give the _choice_ in
33
+ # the case where there should be no arglist (and thus no choice symbol to
34
+ # attach it to).
35
+ def initialize(value_holder, choice=nil)
36
+ @value_holder = value_holder
37
+ @choice = choice
38
+ end
39
+
40
+ # This method takes the argument list, an array, and puts it into
41
+ # the <code>value_holder</code>.
42
+ def fill(arglist); subclass_responsibility; end
43
+
44
+ # Given _conversions_map_, a list of Conversion, select which apply to the arglist,
45
+ # removing them from the hash.
46
+ def claim_conversions(conversions_map)
47
+ @claimed_conversions = []
48
+ end
49
+
50
+ # Apply the claimed conversions to the value previously stored in claim_conversions.
51
+ def apply_claimed_conversions
52
+ # None claimed by default
53
+ end
54
+
55
+ # Apply any effects of changes to the arglist to the result for all the choices.
56
+ def adjust(all_choices)
57
+ # By default, do nothing.
58
+ end
59
+
60
+ # public for testing.
61
+ def arglist_arity_error(length, arglist_arity) # :nodoc:
62
+ plural = length==1 ? '' : 's'
63
+ expected = case arglist_arity
64
+ when Integer
65
+ arglist_arity.to_s
66
+ when Range
67
+ if arglist_arity.end == arglist_arity.begin.succ
68
+ "#{arglist_arity.begin} or #{arglist_arity.end}"
69
+ else
70
+ arglist_arity.in_words
71
+ end
72
+ else
73
+ arglist_arity.inspect
74
+ end
75
+ "#{length} argument#{plural} given, #{expected} expected."
76
+ end
77
+
78
+
79
+ protected
80
+
81
+ def claim_length_check(conversions_map)
82
+ @length_check = conversions_map[@choice].find { |c| c.does_length_check? }
83
+ if @length_check
84
+ conversions_map[@choice].reject { |c| c.does_length_check? }
85
+ end
86
+ end
87
+
88
+
89
+ end
90
+
91
+ # An AbstractArglistStrategy that rejects any non-empty arglist.
92
+ class NoArguments < AbstractArglistStrategy # :nodoc:
93
+ def fill(arglist)
94
+ user_claims(arglist.length == 0) do
95
+ "No arguments are allowed."
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ # The arglist is to be treated as a list, possibly with a Conversion that
102
+ # limits its length. It defers processing of an empty arglist until the
103
+ # last possible moment and only does it if there's no other value for the
104
+ # choice symbol.
105
+ class ArbitraryArglist < AbstractArglistStrategy # :nodoc:
106
+ def fill(arglist)
107
+ @value_holder[@choice] = arglist unless arglist.empty?
108
+ end
109
+
110
+ def claim_conversions(conversions_map)
111
+ claim_length_check(conversions_map)
112
+ end
113
+
114
+ def apply_claimed_conversions
115
+ apply_length_check
116
+ end
117
+
118
+ def adjust(all_choices)
119
+ return if @value_holder[@choice]
120
+ return if all_choices.has_key?(@choice)
121
+
122
+ all_choices[@choice] = []
123
+ @value_holder[@choice] = all_choices[@choice]
124
+ apply_length_check
125
+ end
126
+
127
+ private
128
+
129
+ def apply_length_check
130
+ return unless @length_check
131
+ return unless @value_holder[@choice]
132
+
133
+ value = @value_holder[@choice]
134
+ user_claims(@length_check.suitable?(value)) {
135
+ arglist_arity_error(value.length, @length_check.required_length)
136
+ }
137
+ end
138
+ end
139
+
140
+ # General handling for cases where the Arglist isn't treated as a list, but
141
+ # rather as a single (possibly optional) element. Subclasses handle the
142
+ # optional/non-optional case.
143
+ class NonListStrategy < AbstractArglistStrategy # :nodoc:
144
+ def arity; subclass_responsibility; end
145
+
146
+ def fill(arglist)
147
+ case arglist.length
148
+ when 0: # This is not considered an error because another source
149
+ # might fill in the value.
150
+ when 1: @value_holder[@choice] = arglist[0]
151
+ else user_is_bewildered(arglist_arity_error(arglist.length, self.arity))
152
+ end
153
+ end
154
+
155
+ def claim_conversions(conversions_map)
156
+ claim_length_check(conversions_map)
157
+ user_denies(@length_check) {
158
+ "Don't specify the length of an argument list when it's not treated as an array."
159
+ }
160
+ end
161
+ end
162
+
163
+
164
+ class OneRequiredArg < NonListStrategy # :nodoc:
165
+ def arity; 1; end
166
+
167
+ def adjust(all_choices)
168
+ return if all_choices.has_key?(@choice)
169
+ user_is_bewildered(arglist_arity_error(0,1))
170
+ end
171
+
172
+ end
173
+
174
+ class OneOptionalArg < NonListStrategy # :nodoc:
175
+ def arity; 0..1; end
176
+ end
177
+
178
+ end
@@ -0,0 +1,89 @@
1
+ require 's4t-utils'
2
+ include S4tUtils
3
+ require 'enumerator'
4
+
5
+ require 'user-choices/conversions'
6
+ require 'user-choices/sources'
7
+
8
+ module UserChoices
9
+
10
+ # This class accepts a series of source and choice descriptions
11
+ # and then builds a hash-like object that describes all the choices
12
+ # a user has made before (or while) invoking a script.
13
+ class ChoicesBuilder
14
+
15
+ def initialize
16
+ @defaults = {}
17
+ @conversions = {}
18
+ @sources = []
19
+ end
20
+
21
+ # Add the choice named _choice_, a symbol. _args_ is a keyword
22
+ # argument:
23
+ # * <tt>:default</tt> takes a value that is the default value of the _choice_.
24
+ # * <tt>:type</tt> can be given an array of valid string values. These are
25
+ # checked.
26
+ # * <tt>:type</tt> can also be given <tt>:integer</tt>. The value is cast into
27
+ # an integer. If that's impossible, an exception is raised.
28
+ # * <tt>:type</tt> can also be given <tt>:boolean</tt>. The value is converted into
29
+ # +true+ or +false+ (or an exception is raised).
30
+ # * <tt>:type</tt> can also be given <tt>[:string]</tt>. The value
31
+ # will be an array of strings. For example, "--value a,b,c" will
32
+ # produce ['a', 'b', 'c'].
33
+ #
34
+ # You might also give <tt>:length => 5</tt> or <tt>:length => 3..4</tt>. (In
35
+ # this case, a <tt>:type</tt> of <tt>[:string]</tt> is assumed.)
36
+ #
37
+ # The _block_ is passed a CommandLineSource object. It's used
38
+ # to describe the command line.
39
+ def add_choice(choice, args={}, &block)
40
+ # TODO: does the has_key? actually make a difference?
41
+ @defaults[choice] = args[:default] if args.has_key?(:default)
42
+ @conversions[choice] = []
43
+ Conversion.record_for(args[:type], @conversions[choice])
44
+ if args.has_key?(:length)
45
+ Conversion.record_for({:length => args[:length]}, @conversions[choice])
46
+ end
47
+ block.call(ArgForwarder.new(@command_line_source, choice)) if block
48
+ end
49
+
50
+ # This adds a source of choices. The _source_ is a class like
51
+ # CommandLineSource. The <tt>messages_and_args</tt> are sent
52
+ # to a new object of that class.
53
+ def add_source(source_class, *messages_and_args)
54
+ source = source_class.new
55
+ message_sends(messages_and_args).each { | send_me | source.send(*send_me) }
56
+ @sources << source
57
+ @command_line_source = source if source_class == CommandLineSource
58
+ end
59
+
60
+ # Once sources and choices have been described, this builds and
61
+ # returns a hash-like object indexed by the choices.
62
+ def build
63
+ retval = {}
64
+ @sources << DefaultSource.new.use_hash(@defaults)
65
+ @sources.each { |s| s.fill }
66
+ @sources.each { |s| s.apply(@conversions) }
67
+ @sources.reverse.each { |s| retval.merge!(s) }
68
+ @sources.each { |s| s.adjust(retval) }
69
+ retval
70
+ end
71
+
72
+ # Public for testing.
73
+
74
+ def message_sends(messages_and_args) # :nodoc:
75
+ where_at = symbol_indices(messages_and_args)
76
+ where_end = where_at[1..-1] + [messages_and_args.length]
77
+ where_at.to_enum(:each_with_index).collect do |start, where_end_index |
78
+ messages_and_args[start...where_end[where_end_index]]
79
+ end
80
+ end
81
+
82
+ def symbol_indices(array) # :nodoc:
83
+ array.to_enum(:each_with_index).collect do |obj, index|
84
+ index if obj.is_a?(Symbol)
85
+ end.compact
86
+ end
87
+ end
88
+
89
+ end