cli_miami 0.0.9 → 1.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 47e57b45bdc22855bb5fae82dcc3dd12c54a7122
4
- data.tar.gz: d13decde950c513d8bb42ffe54f6fc987c55927b
3
+ metadata.gz: 4027d96f33ad46d4e0859f84b1c9399ce569a8d3
4
+ data.tar.gz: b1442d5a721d9cedfd612ac86d73057617b2b808
5
5
  SHA512:
6
- metadata.gz: c06ffeeaa5f1afdf1614ae4d0ad6fd7a99226f70471a27ad93d61d6189eb56e8cc99b8c6213e1cf3da450102714a858ae6dbbd09c12b5786ee66c7e2f97c08a8
7
- data.tar.gz: 89e5be978ec19120b0eb269c18eea67159cfcad43c16177e10c1705e116669f76a5be73e153c76dd30195e966191db5a06da4f859dd5844a2b678a153829fd37
6
+ metadata.gz: 1f7b627d647f53c1f8cfd48cc3adbc5a02dbecdbf61a3581eeb3b24879d3b60cbecdee265b2461c6f08f7576082c2f9c23b1e8a032413a891fee5dc5f4e5fbeb
7
+ data.tar.gz: 2be499f83f714ac02761f01baa93e5fe0f2a133f78022700bd665490ca72044183504d59dfacd13534e3e46a23fed18076b90b54e2785bb11d8e78be4e89ef21
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- [![Gem Version](https://badge.fury.io/rb/cli_miami.svg)](http://badge.fury.io/rb/cli_miami)
2
- [![Build Status](https://travis-ci.org/brewster1134/CLI-Miami.svg)](https://travis-ci.org/brewster1134/CLI-Miami)
3
- [![Coverage Status](https://coveralls.io/repos/brewster1134/CLI-Miami/badge.png)](https://coveralls.io/r/brewster1134/CLI-Miami)
1
+ [![gem version](https://badge.fury.io/rb/CLI-Miami.svg)](https://rubygems.org/gems/CLI-Miami)
2
+ [![dependencies](https://gemnasium.com/brewster1134/CLI-Miami.svg)](https://gemnasium.com/brewster1134/CLI-Miami)
3
+ [![docs](http://inch-ci.org/github/brewster1134/CLI-Miami.svg?branch=master)](http://inch-ci.org/github/brewster1134/CLI-Miami)
4
+ [![build](https://travis-ci.org/brewster1134/CLI-Miami.svg?branch=master)](https://travis-ci.org/brewster1134/CLI-Miami)
5
+ [![coverage](https://coveralls.io/repos/brewster1134/CLI-Miami/badge.svg?branch=master)](https://coveralls.io/r/brewster1134/CLI-Miami?branch=master)
6
+ [![code climate](https://codeclimate.com/github/brewster1134/CLI-Miami/badges/gpa.svg)](https://codeclimate.com/github/brewster1134/CLI-Miami)
7
+ [![omniref](https://www.omniref.com/github/brewster1134/CLI-Miami.png)](https://www.omniref.com/github/brewster1134/CLI-Miami)
4
8
 
5
9
  # CLI Miami
6
10
  A feature rich alternative for `gets` and `puts` for your cli interface
@@ -85,3 +89,5 @@ yuyi -m https://raw.githubusercontent.com/brewster1134/CLI-Miami/master/yuyi_men
85
89
  bundle install
86
90
  bundle exec guard
87
91
  ```
92
+
93
+ [![WTFPL](http://www.wtfpl.net/wp-content/uploads/2012/12/wtfpl-badge-4.png)](http://www.wtfpl.net)
data/i18n/en.yml ADDED
@@ -0,0 +1,34 @@
1
+ en:
2
+ cli_miami:
3
+ # CliMiami core library strings
4
+ core:
5
+ empty: '[EMPTY]'
6
+ enter_end_value: 'Enter end value'
7
+ enter_start_value: 'Enter starting value'
8
+ enter_value_for: 'Enter a value for %{key}'
9
+ no_description: No description provided
10
+
11
+ errors:
12
+ # generic error type messages
13
+ convert: "`%{value}` cannot be converted to `%{type}`. Allowed values are: `%{allowed_values}` (%{description})"
14
+ length: "`%{value}` has a length of %{value_length}, but must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
15
+ regexp: "`%{value}` does not match the required format. Allowed values are: `%{allowed_values}` (%{description})"
16
+ type: "Unknown type %{type}"
17
+
18
+ # specific value type messages
19
+ # these messages will take precedence over the general messages above
20
+ array:
21
+ length: "Your array (%{value}) has %{value_length} items, but needs between %{allowed_values} (%{description})"
22
+ boolean:
23
+ file:
24
+ validate: "`%{value}` is not a valid path. Allowed values are: `%{allowed_values}` (%{description})"
25
+ fixnum:
26
+ length: "`%{value}` must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
27
+ float:
28
+ length: "`%{value}` must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
29
+ hash:
30
+ length: "Your object (%{value}) has %{value_length} items, but needs between %{allowed_values} (%{description})"
31
+ keys: "The object has `%{value}` set, but is missing `%{keys}`. Allowed values are: `%{allowed_values}` (%{description})"
32
+ range:
33
+ string:
34
+ symbol:
data/lib/cli_miami/ask.rb CHANGED
@@ -1,54 +1,194 @@
1
1
  #
2
- # class CliMiami::Ask
2
+ # class CliMiami::A
3
3
  #
4
-
5
- require 'readline'
6
-
7
4
  class CliMiami::A
8
- @@prompt = '>>> '
5
+ attr_reader :value
9
6
 
7
+ # A.sk API method
8
+ #
10
9
  # See documentation for CliMiami::S.ay
11
- # The same options are accepted
10
+ # The same options are accepted, with the addition of
11
+ # :readline - uses Readline instead of standard `gets`
12
+ # :type - symbol specifying what type of data is requested from the user
13
+ # :validate - hash of validation options
12
14
  #
13
15
  def self.sk question, options = {}, &block
14
- # set default options
15
- @options = {
16
- :readline => false
17
- }
18
-
19
- # merge preset options
20
- if options.is_a? Symbol
21
- @options.merge! CliMiami.presets[options]
22
- elsif preset = options.delete(:preset)
23
- @options.merge! CliMiami.presets[preset]
24
- end
16
+ new question, options, &block
17
+ end
25
18
 
26
- # merge remaining options
27
- if options.is_a? Hash
28
- @options.merge! options
29
- end
19
+ private
30
20
 
31
- CliMiami::S.ay question, @options
21
+ def initialize question, options
22
+ options = CliMiami.get_options options
32
23
 
33
- output = if @options[:readline]
34
- Readline.readline(@@prompt).chomp('/')
35
- else
36
- CliMiami::S.ay @@prompt, :newline => false
37
- $stdin.gets
38
- end.rstrip
24
+ # add description to question
25
+ question << ' (' << options[:description] << ')'
26
+
27
+ # display question
28
+ CliMiami::S.ay question, options
29
+
30
+ # request given type to user
31
+ @value = request_type options
39
32
 
40
33
  # return response if no block is passed
34
+ # rubocop:disable Style/GuardClause
41
35
  if block_given?
42
- yield output
36
+ yield @value
43
37
  else
44
- return output
38
+ return @value
45
39
  end
40
+ # rubocop:enable Style/GuardClause
46
41
  end
47
42
 
48
- private
43
+ # determine the expecting type, and request input form user
44
+ #
45
+ def request_type options
46
+ send("request_#{options[:type]}", options)
47
+ rescue
48
+ CliMiami::S.ay I18n.t('cli_miami.errors.type', options)
49
+ end
50
+
51
+ # for most types, a simple validation check is all that is needed
52
+ # if validation fails, we request the user to try again
53
+ #
54
+ # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity
55
+ def request_until_valid options, allow_empty_string = false
56
+ response = nil
57
+
58
+ while response.nil?
59
+ # get user input based on given file type
60
+ response = if options[:type] == :file
61
+ Readline.readline.chomp '/'
62
+ else
63
+ $stdin.gets.chomp
64
+ end
65
+
66
+ # for multiple entry type objects, we allow the user to justs press enter to finish adding entries
67
+ break if allow_empty_string && response == ''
68
+
69
+ # otherwise validate the user's input
70
+ validation = CliMiami::Validation.new response, options
71
+ if validation.valid?
72
+ response = validation.value
73
+ else
74
+ response = nil
75
+ CliMiami::S.ay validation.error, :cli_miami_fail
76
+ end
77
+ end
78
+
79
+ response
80
+ end
81
+ # rubocop:enable Metrics/MethodLength, Metrics/PerceivedComplexity
82
+ alias_method :request_boolean, :request_until_valid
83
+ alias_method :request_file, :request_until_valid
84
+ alias_method :request_float, :request_until_valid
85
+ alias_method :request_fixnum, :request_until_valid
86
+ alias_method :request_string, :request_until_valid
87
+ alias_method :request_symbol, :request_until_valid
88
+
89
+ # rubocop:disable Metrics/MethodLength
90
+ def request_array options
91
+ array = []
92
+ value_options = CliMiami.get_options(options[:value_options] || {})
93
+
94
+ # build the array by prompting the user
95
+ # until the array length is an acceptable length, keep prompting user for values
96
+ while array.length < options[:max]
97
+ response = request_until_valid value_options, true
98
+
99
+ if response.empty?
100
+ break if CliMiami::Validation.new(array, options).valid?
101
+ redo
102
+ else
103
+ array << response
104
+ end
49
105
 
50
- def self.prompt; @@prompt; end
51
- def self.prompt= prompt
52
- @@prompt = prompt
106
+ # update user
107
+ CliMiami::S.ay array.to_sentence, :cli_miami_success
108
+ end
109
+
110
+ array
111
+ end
112
+
113
+ # rubocop:disable Metrics/AbcSize, Metrics/BlockNesting, Metrics/PerceivedComplexity
114
+ def request_hash options
115
+ hash = {}
116
+ options[:keys] ||= []
117
+ value_options = CliMiami.get_options(options[:value_options] || {})
118
+ required_keys_set = false
119
+
120
+ # build the hash by prompting the user
121
+ # until the hash length of keys is an acceptable length, keep prompting user for values
122
+ while hash.keys.length < options[:max]
123
+ # if keys options is set, prompt for those values first
124
+ if required_keys_set == false
125
+ options[:keys].each do |key|
126
+ hash[key.to_sym] = request_until_valid value_options
127
+
128
+ # update user
129
+ CliMiami::S.ay hash.to_s, :cli_miami_success
130
+ end
131
+
132
+ # set boolean so we know all required keys are set
133
+ required_keys_set = true
134
+
135
+ # end this loop to re-check the while condition to make sure the max wasn't reached from required keys
136
+ # e.g. setting { max: 2, keys: [:foo, :bar] }
137
+ # this prevents users from entering user-defined keys since the max will already be met
138
+ next
139
+
140
+ # then start prompting for keys and values
141
+ else
142
+ # request key
143
+ user_key = request_until_valid value_options.merge(type: :symbol), true
144
+
145
+ if user_key.empty?
146
+ break if CliMiami::Validation.new(hash, options).valid?
147
+ redo
148
+ else
149
+ # request value
150
+ CliMiami::S.ay I18n.t('cli_miami.core.enter_value_for', key: user_key), :cli_miami_success
151
+ hash[user_key] = request_until_valid value_options, true
152
+ end
153
+ end
154
+
155
+ CliMiami::S.ay hash.to_s, :cli_miami_success
156
+ end
157
+
158
+ hash
159
+ end
160
+ # rubocop:enable Metrics/AbcSize, Metrics/BlockNesting, Metrics/PerceivedComplexity
161
+
162
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
163
+ def request_range options
164
+ start_value = nil
165
+ end_value = nil
166
+ range_value_options = CliMiami.get_options type: :float
167
+
168
+ # get start value
169
+ until (Float(start_value) rescue nil)
170
+ CliMiami::S.ay I18n.t('cli_miami.core.enter_start_value'), preset: :cli_miami_success, newline: false
171
+ start_value = request_until_valid range_value_options
172
+ end
173
+
174
+ # get end value
175
+ until (Float(end_value) rescue nil)
176
+ CliMiami::S.ay I18n.t('cli_miami.core.enter_end_value'), preset: :cli_miami_success, newline: false
177
+ end_value = request_until_valid range_value_options
178
+ end
179
+
180
+ # swap values if entered in reverse
181
+ start_value, end_value = end_value, start_value if start_value > end_value
182
+
183
+ # build range object
184
+ range = Range.new start_value, end_value
185
+
186
+ # if range is invalid, start over and request it again
187
+ if CliMiami::Validation.new(range, options).valid?
188
+ range
189
+ else
190
+ request_range options
191
+ end
53
192
  end
193
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
54
194
  end
@@ -0,0 +1,84 @@
1
+ #
2
+ # 3rd party libraries & configuration
3
+ #
4
+ require 'active_support/core_ext/hash/conversions'
5
+ require 'active_support/core_ext/hash/keys'
6
+ require 'active_support/core_ext/string/inflections'
7
+ require 'i18n'
8
+ require 'readline'
9
+
10
+ # core object overrides
11
+ #
12
+ class Hash
13
+ def to_s
14
+ map do |k, v|
15
+ "#{k}: #{v}"
16
+ end.to_sentence
17
+ end
18
+ end
19
+
20
+ # i18n
21
+ #
22
+ # load yml locales included in the cli miami gem
23
+ # [PATH TO GEMS]/cli_miami/i18n/en.yml
24
+ I18n.load_path += Dir["#{File.dirname(__FILE__)}/../i18n/*.yml"]
25
+ # load locale in current directory named `i18n.yml`
26
+ # ./i18n.yml
27
+ I18n.load_path += Dir['./i18n.yml']
28
+ # load locales in the folder `i18n` in the current directory
29
+ # ./i18n/en.yml
30
+ I18n.load_path += Dir['./i18n/*.yml']
31
+
32
+ # readline
33
+ #
34
+ Readline.completion_append_character = '/'
35
+
36
+ # creates Boolean type that true/false inherit
37
+ # this allows true/false to respond to a single Boolean class to check for
38
+ #
39
+ # rubocop:disable Style/Documentation
40
+ module Boolean; end
41
+ class TrueClass; include Boolean; end
42
+ class FalseClass; include Boolean; end
43
+ # rubocop:enable Style/Documentation
44
+
45
+ # global configuration and setup
46
+ #
47
+ module CliMiami::Core
48
+ BOOLEAN_TRUE_VALUES = %w(true t yes y).freeze
49
+ BOOLEAN_FALSE_VALUES = %w(false f no n).freeze
50
+
51
+ # keys: type values allowed through the API
52
+ # values: type used to validate internally in the Validation class
53
+ TYPE_MAP = {
54
+ array: :array,
55
+ boolean: :boolean,
56
+ dir: :file,
57
+ directory: :file,
58
+ file: :file,
59
+ fixnum: :fixnum,
60
+ float: :float,
61
+ folder: :file,
62
+ hash: :hash,
63
+ integer: :fixnum,
64
+ list: :array,
65
+ number: :fixnum,
66
+ object: :hash,
67
+ path: :file,
68
+ range: :range,
69
+ string: :string,
70
+ symbol: :symbol
71
+ }.freeze
72
+
73
+ # default presets
74
+ # rubocop:disable Style/ClassVars
75
+ @@presets = {
76
+ cli_miami_fail: {
77
+ color: :red
78
+ },
79
+ cli_miami_success: {
80
+ color: :green
81
+ }
82
+ }
83
+ # rubocop:enable Style/ClassVars
84
+ end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Build custom i18n error messages
3
+ #
4
+ class CliMiami::Error
5
+ attr_reader :message
6
+
7
+ private
8
+
9
+ def initialize value, options, *error_keys
10
+ options[:value] = convert_type_to_string value
11
+ options[:allowed_values] = allowed_values options
12
+
13
+ @message = i18n_lookup_keys(options, *error_keys) || 'Unknown Error'
14
+ end
15
+
16
+ # convert value from it's type, to a user friendly string
17
+ # with core ruby objects, we create a `.to_string` method instead of changes the default `.to_s` method
18
+ #
19
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
20
+ def convert_type_to_string value
21
+ case value
22
+
23
+ when Array
24
+ value.map do |e|
25
+ if e.nil? || e == ''
26
+ I18n.t 'cli_miami.core.empty'
27
+ else
28
+ convert_type_to_string e
29
+ end
30
+ end.to_sentence
31
+
32
+ when Hash
33
+ value.map do |k, v|
34
+ v = if v.nil? || v == ''
35
+ I18n.t 'cli_miami.core.empty'
36
+ else
37
+ convert_type_to_string v
38
+ end
39
+
40
+ "#{k}: #{v}"
41
+ end.to_sentence
42
+
43
+ when Range
44
+ "#{value.min}-#{value.max}"
45
+
46
+ when File
47
+ File.expand_path value
48
+
49
+ else
50
+ value.to_s
51
+
52
+ end
53
+ end
54
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
55
+
56
+ # get allowed values based on validation details
57
+ #
58
+ # rubocop:disable Metrics/MethodLength
59
+ def allowed_values options
60
+ case options[:type]
61
+ when :array, :hash
62
+ if options[:value_options]
63
+ allowed_values options[:value_options]
64
+ else
65
+ "#{options[:min]}-#{options[:max]}"
66
+ end
67
+
68
+ when :boolean
69
+ all_boolean_values = CliMiami::BOOLEAN_TRUE_VALUES + CliMiami::BOOLEAN_FALSE_VALUES
70
+ all_boolean_values.to_sentence
71
+
72
+ when :fixnum, :float, :range, :string, :symbol
73
+ "#{options[:min]}-#{options[:max]}"
74
+
75
+ when :regexp
76
+ options[:regexp].inspect
77
+
78
+ else
79
+ '?'
80
+ end
81
+ end
82
+ # rubocop:enable Metrics/MethodLength
83
+
84
+ # build an i18n dot notation string to use for lookup in the language yaml
85
+ #
86
+ # rubocop:disable Style/ClosingParenthesisIndentation
87
+ def i18n_lookup_keys options, *error_keys
88
+ # check for type specific error first, then look for generic errors
89
+ i18n_lookup(options,
90
+ 'cli_miami',
91
+ 'errors',
92
+ options[:type].to_s,
93
+ *error_keys
94
+ ) || i18n_lookup(options,
95
+ 'cli_miami',
96
+ 'errors',
97
+ *error_keys
98
+ )
99
+ end
100
+ # rubocop:enable Style/ClosingParenthesisIndentation
101
+
102
+ # use i18n dot notation keys to lookup string
103
+ #
104
+ def i18n_lookup options, *keys
105
+ i18n_string = keys.flatten.compact.each(&:to_s).join('.')
106
+ I18n.t i18n_string, options if I18n.exists? i18n_string
107
+ end
108
+ end
data/lib/cli_miami/say.rb CHANGED
@@ -1,13 +1,15 @@
1
- #
2
- # class CliMiami::Say
3
- #
4
1
  require 'term/ansicolor'
5
2
 
3
+ # Reopen String class for ANSI color support
6
4
  class String
7
5
  include Term::ANSIColor
8
6
  end
9
7
 
8
+ #
9
+ # class CliMiami::S.ay
10
+ #
10
11
  class CliMiami::S
12
+ # S.ay API method
11
13
  #
12
14
  # @param options [Symbol or Hash] options can be preset symbol, or a hash of options.
13
15
  #
@@ -18,7 +20,7 @@ class CliMiami::S
18
20
  # color: => [symbol] See README for ansi color codes
19
21
  # bgcolor: => [symbol] See README for ansi color codes
20
22
  # style: => [symbol] See README for ansi style codes
21
- # justify: => [center|ljust|rjust] The type of justification to use
23
+ # justify: => [center|left|right] The type of justification to use
22
24
  # padding: => [integer] The maximum string size to justify text in
23
25
  # indent: => [integer] The number of characters to indent
24
26
  # newline: => [boolean] True if you want a newline after the output
@@ -27,75 +29,83 @@ class CliMiami::S
27
29
  # @return
28
30
  #
29
31
  def self.ay text = '', options = {}
30
- # backup text and convert it to a string
31
- original_text = text = text.to_s
32
-
33
32
  # set default options
34
- @options = {
35
- :style => [],
36
- :newline => true
37
- }
33
+ options = CliMiami.get_options options
34
+ new text, options
35
+ end
38
36
 
39
- # merge preset options
40
- if options.is_a? Symbol
41
- @options.merge! CliMiami.presets[options]
42
- elsif preset = options.delete(:preset)
43
- @options.merge! CliMiami.presets[preset]
44
- end
37
+ private
45
38
 
46
- # merge remaining options
47
- if options.is_a? Hash
48
- @options.merge! options
49
- end
39
+ def initialize text, options
40
+ @text = text
41
+ @options = options
50
42
 
51
- # convert single style into an array for processing
52
- if !@options[:style].is_a? Array
53
- @options[:style] = [@options[:style]]
54
- end
43
+ # formatter methods
44
+ modify_justification!
45
+ modify_text_color!
46
+ modify_background_color!
47
+ modify_styles!
48
+ modify_indentation!
49
+ modify_overwrite!
55
50
 
56
- # Justify/Padding options
57
- if @options[:justify] && @options[:padding]
58
- text = text.send @options[:justify], @options[:padding]
59
- end
51
+ # render formatted text to user
52
+ print_output
53
+ end
54
+
55
+ # Justify/Padding options
56
+ # must convert option to support String class method
57
+ def modify_justification!
58
+ justify =
59
+ case @options[:justify]
60
+ when :center then :center
61
+ when :right then :rjust
62
+ else :ljust
63
+ end
64
+
65
+ @text = @text.send justify, @options[:padding]
66
+ end
60
67
 
61
- # Set foreground color
68
+ # Set foreground color
69
+ def modify_text_color!
62
70
  if @options[:color]
63
71
  # if bright style is passed, use the bright color variation
64
- text = if @options[:style].delete :bright
65
- text.send("bright_#{@options[:color]}")
72
+ @text = if @options[:style].delete :bright
73
+ @text.send "bright_#{@options[:color]}"
66
74
  else
67
- text.send(@options[:color])
75
+ @text.send @options[:color]
68
76
  end
69
77
  end
78
+ end
70
79
 
71
- # Set background color
72
- if @options[:bgcolor]
73
- text = text.send("on_#{@options[:bgcolor]}")
74
- end
80
+ # Set background color
81
+ def modify_background_color!
82
+ @text = @text.send("on_#{@options[:bgcolor]}") if @options[:bgcolor]
83
+ end
75
84
 
76
- # Apply all styles
85
+ # Apply all styles
86
+ def modify_styles!
77
87
  @options[:style].each do |style|
78
- text = text.send(style)
88
+ @text = @text.send(style)
79
89
  end
90
+ end
80
91
 
81
- # Indent
82
- if @options[:indent]
83
- text = (' ' * @options[:indent]) + text
84
- end
92
+ # Indent
93
+ def modify_indentation!
94
+ @text = (' ' * @options[:indent]) + @text if @options[:indent]
95
+ end
85
96
 
86
- # Flag text to be overwritten by next text written
87
- if @options[:overwrite]
88
- text = "#{text}\r"
89
- end
97
+ # Flag text to be overwritten by next text written
98
+ def modify_overwrite!
99
+ @text = "#{@text}\r" if @options[:overwrite]
100
+ end
90
101
 
102
+ # finally render the formatted text to the screen
103
+ def print_output
91
104
  # Determine if a newline should be used
92
105
  if !@options[:newline] || @options[:overwrite]
93
- $stdout.print text
106
+ $stdout.print @text + ' '
94
107
  else
95
- $stdout.puts text
108
+ $stdout.puts @text
96
109
  end
97
-
98
- # return original text
99
- return original_text
100
110
  end
101
111
  end