questions 1.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 41e385aa0a2fa87149846bfa99223235a5910887
4
+ data.tar.gz: 5aa6e4a359aad4dc0bfa7232b79daabc4a746934
5
+ SHA512:
6
+ metadata.gz: c1056f0f6f023509511d95659796f301be00924d129e2c2a38cdffed01df1899b2adf275367c1e662c5dfc959bd2c874a323fce5ce66a2dae997ee013b145d08
7
+ data.tar.gz: c74a6eca07e5d9a80327b2df544bae4bd9adfe042479a37eb9e1d109266d1b781a2d10022842d990a69866a7e6674bd2db36a9be8f96b2d11cbc3eea60ddddfa
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in questions.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 DSIW
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Question
2
+
3
+ Ask human a question. She or he has to select one answer.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'questions'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install questions
18
+
19
+ ## Usage
20
+
21
+ ### Preamble
22
+ ``` ruby
23
+ require "questions"
24
+ include Questions
25
+ ```
26
+
27
+ ### Long way
28
+ ``` ruby
29
+ q = Question.new("File does exist, what should be done?")
30
+ q.answers = [:skip, :overwrite, :abort]
31
+ answer = q.ask
32
+ # SHELL:
33
+ # $ File does exist, what should be done? [s]kip, [o]verwrite, [a]bort
34
+ # s<enter>
35
+ answer #=> :skip
36
+ ```
37
+
38
+ ### Short way
39
+ ``` ruby
40
+ answer = Question.ask "File does exist, what should be done?", [:skip, :overwrite, :abort]
41
+ # SHELL:
42
+ # $ File does exist, what should be done? [s]kip, [o]verwrite, [a]bort
43
+ # s<enter>
44
+ answer #=> :skip
45
+ ```
46
+
47
+ ### Hashish way
48
+ ``` ruby
49
+ answer = Question.ask "File does exist, what should be done?", skip: true, overwrite: false, abort: true
50
+ # SHELL:
51
+ # $ File does exist, what should be done? [s]kip, [a]bort
52
+ # s<enter>
53
+ answer #=> :skip
54
+ ```
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/clean'
4
+ require 'rake/version_task'
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new do |t|
9
+ # Put spec opts in a file named .rspec in root
10
+ end
11
+
12
+ Rake::VersionTask.new
13
+
14
+ task :doc => [ 'doc:clean', 'doc:api' ]
15
+
16
+ CLOBBER << "doc"
17
+ CLEAN << "doc"
18
+ namespace :doc do
19
+ require 'yard'
20
+ YARD::Rake::YardocTask.new(:api) do |t|
21
+ t.files = ['lib/**/*.rb']
22
+ t.options = [
23
+ '--output-dir', 'doc/api',
24
+ '--markup', 'markdown'
25
+ ]
26
+ end
27
+
28
+ desc 'Remove YARD Documentation'
29
+ task :clean do
30
+ system("rm -rf #{File.dirname(__FILE__)}/doc/api")
31
+ end
32
+ end
33
+
34
+ task :default => [:spec]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
@@ -0,0 +1,129 @@
1
+ # encoding: utf-8
2
+
3
+ module Questions
4
+ class Answer
5
+ SPECIAL_ENDINGS = [:all]
6
+
7
+ # Instantiates new Answer object.
8
+ #
9
+ # Only the first key value pair will be used.
10
+ #
11
+ # @param [Hash] answer_hash Key (instruction); value (activeness) is `true` or `false`.
12
+ #
13
+ # @example Hash with one item
14
+ # Answer.new({:overwrite => true}) #=> [o]verwrite
15
+ # Answer.new(:overwrite => true) #=> [o]verwrite
16
+ # Answer.new(:overwrite) #=> [o]verwrite
17
+ # Answer.new({:overwrite => false}) #=> ""
18
+ # Answer.new(:overwrite => false) #=> ""
19
+ # Answer.new({:overwrite => nil}) #=> ""
20
+ #
21
+ # @example Items of hash with more than one item will be ignored
22
+ # Answer.new({:overwrite => true, :overwrite_all => false}) #=> [o]verwrite
23
+ # Answer.new({:overwrite => true, :overwrite_all => true}) #=> [o]verwrite
24
+ def initialize(*answer_hash)
25
+ hash = answer_hash.first
26
+ hash = {hash => true} if hash.is_a? Symbol
27
+
28
+ unless [Hash, Symbol].any? { |class_name| hash.is_a? class_name }
29
+ raise ArgumentError, "Parameter has to be a key-value-pair or a symbol"
30
+ end
31
+
32
+ array = hash.first
33
+ @instruction = array[0]
34
+ @active = array[1]
35
+ end
36
+
37
+ # Gets instruction
38
+ #
39
+ # @example Hash with one item
40
+ # Answer.new(overwrite: true).instruction #=> :overwrite
41
+ # Answer.new(overwrite: false).instruction #=> :overwrite
42
+ attr_reader :instruction
43
+
44
+ # Is answer active?
45
+ #
46
+ # @example Hash with one item
47
+ # Answer.new(overwrite: true).active? #=> true
48
+ # Answer.new(overwrite: false).active? #=> false
49
+ #
50
+ # @see {true?}
51
+ def active?
52
+ @active
53
+ end
54
+ alias :true? :active?
55
+
56
+ # Is answer inactive?
57
+ #
58
+ # @example Hash with one item
59
+ # Answer.new(overwrite: true).inactive? #=> false
60
+ # Answer.new(overwrite: false).inactive? #=> true
61
+ #
62
+ # @see {false?}
63
+ def inactive?
64
+ !active?
65
+ end
66
+ alias :false? :inactive?
67
+
68
+ # Sets indicator
69
+ #
70
+ # @example Hash with one item
71
+ # answer = Answer.new(overwrite: true)
72
+ # answer.indicator = :ov
73
+ # answer.indicator #=> :ov
74
+ attr_writer :indicator
75
+
76
+ # Gets indicator
77
+ #
78
+ # @example with true answer
79
+ # a = Answer.new(overwrite: true)
80
+ # a.indicator #=> :o
81
+ # a.indicator(2) #=> :ov
82
+ #
83
+ # @example with special true answer
84
+ # Answer.new(overwrite_all: true).indicator(2) #=> :OV
85
+ #
86
+ # @example with inactive answer
87
+ # Answer.new(overwrite: false).indicator #=> nil
88
+ #
89
+ # @example set indicator
90
+ # a = Answer.new(overwrite: true)
91
+ # a.indicator #=> :o
92
+ # a.indicator = :ov
93
+ # a.indicator #=> :ov
94
+ def indicator(first_chars=1)
95
+ return nil if inactive?
96
+ return @indicator if @indicator
97
+ indicator = instruction.to_s[0...first_chars]
98
+ indicator.upcase! if special?
99
+ indicator.to_sym
100
+ end
101
+
102
+ # Returns `true` if answer is special. Special answers are answers which ends with one of the {SPECIAL_ENDINGS}, `false` otherwise.
103
+ def special?
104
+ SPECIAL_ENDINGS.any? { |ending| instruction.to_s =~ /#{ending}$/ }
105
+ end
106
+
107
+ # Gets indicator hash
108
+ #
109
+ # @example Hash with one item
110
+ # Answer.new(overwrite: true).indicator_hash #=> {:o => :overwrite}
111
+ # Answer.new(overwrite: false).indicator_hash #=> nil
112
+ def indicator_hash
113
+ return nil if inactive?
114
+ {indicator => instruction}
115
+ end
116
+
117
+ # Gets string representation
118
+ #
119
+ # @example Hash with one item
120
+ # Answer.new(overwrite: true).to_s #=> "[o]verwrite"
121
+ # Answer.new(overwrite: false).to_s #=> ""
122
+ def to_s
123
+ return nil if inactive?
124
+ instruction_without_indicator = instruction.to_s[indicator.length..-1]
125
+ humanized = instruction_without_indicator.gsub("_", " ")
126
+ "[#{indicator}]#{humanized}"
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,196 @@
1
+ # encoding: utf-8
2
+
3
+ module Questions
4
+ class Answers < Array
5
+ # Instantiates a new Answers object.
6
+ #
7
+ # @param args [Answer, Symbol, Array, Hash] Initial answers
8
+ #
9
+ # Each parameter will be send to {#<<}.
10
+ #
11
+ # @example parameter as Answer
12
+ # answers = Answers.new Answer.new(:abort)
13
+ # answers.first.instruction #=> :abort
14
+ # answers.first.active #=> true
15
+ #
16
+ # @example parameter as Symbol
17
+ # answers = Answers.new :abort
18
+ # answers.first.instruction #=> :abort
19
+ # answers.first.active #=> true
20
+ #
21
+ # @example parameter as Array
22
+ # answers = Answers.new [:abort, Answer.new(:overwrite)]
23
+ # answers.first.instruction #=> :abort
24
+ # answers.first.active #=> true
25
+ # answers.last.instruction #=> :overwrite
26
+ # answers.last.active #=> true
27
+ #
28
+ # @example parameter as Hash
29
+ # answers = Answers.new({abort: true, overwrite: false, "non-symbol" => "ignored"})
30
+ # answers.size #=> 2
31
+ # answers.first.instruction #=> :abort
32
+ # answers.first.active #=> true
33
+ # answers.last.instruction #=> :overwrite
34
+ # answers.last.active #=> false
35
+ def initialize(*args)
36
+ args.each { |arg| self << arg }
37
+ end
38
+
39
+ # Adds answer. Answer can be an instance of {Answer}, {Symbol}, a {Hash} or an {Array}. Chaining is
40
+ # supported.
41
+ #
42
+ # @example parameter as Answer
43
+ # answers = Answers.new
44
+ # answers << Answer.new(:abort)
45
+ # answers.first.instruction #=> :abort
46
+ # answers.first.active #=> true
47
+ #
48
+ # @example parameter as Symbol
49
+ # answers = Answers.new
50
+ # answers << :abort << :overwrite
51
+ # answers.first.instruction #=> :abort
52
+ # answers.first.active #=> true
53
+ # answers.last.instruction #=> :overwrite
54
+ # answers.last.active #=> true
55
+ #
56
+ # @example parameter as Array
57
+ # answers = Answers.new
58
+ # answers << [:abort, Answer.new(:overwrite)]
59
+ # answers.first.instruction #=> :abort
60
+ # answers.first.active #=> true
61
+ # answers.last.instruction #=> :overwrite
62
+ # answers.last.active #=> true
63
+ #
64
+ # @example parameter as Hash
65
+ # answers = Answers.new
66
+ # answers << {abort: true, overwrite: false, "non-symbol" => "ignored"}
67
+ # answers.size #=> 2
68
+ # answers.first.instruction #=> :abort
69
+ # answers.first.active #=> true
70
+ # answers.last.instruction #=> :overwrite
71
+ # answers.last.active #=> false
72
+ #
73
+ # @return [Answers] self
74
+ def <<(arg)
75
+ case arg
76
+ when Answer then super arg
77
+ when Symbol then super Answer.new(arg)
78
+ when Array
79
+ arg.each { |arg| self << arg } # call as Answer or Symbol
80
+ when Hash
81
+ self << convert_hash_to_answers(select_symbols(arg)) # call as Array
82
+ end
83
+ self
84
+ end
85
+
86
+ # Gets answer by indicator.
87
+ #
88
+ # @example
89
+ # answers = Answers.new [:abort, :overwrite]
90
+ # answers[:a].instruction #=> :abort
91
+ # answers[:o].instruction #=> :overwrite
92
+ #
93
+ # @return [Answer]
94
+ def [](indicator)
95
+ update_indicators_for_uniqueness!
96
+ select { |answer| answer.indicator == indicator }.first
97
+ end
98
+
99
+ # Updates indicators to get all unique.
100
+ #
101
+ # @example
102
+ # answers = Answers.new [:abort, :abort_all, :abc]
103
+ # answers.indicators #=> [:a, :A, :a]
104
+ # answers.has_unique_indicators? #=> false
105
+ # answers.update_indicators_for_uniqueness!
106
+ # answers.indicators #=> [:a, :A, :ab]
107
+ # answers.has_unique_indicators? #=> true
108
+ def update_indicators_for_uniqueness!
109
+ return if has_unique_indicators?
110
+
111
+ each_with_index do |answer, i|
112
+ other_indicators = first(i).map(&:indicator)
113
+ answer.indicator = free_indicator_of(answer, used_indicators: other_indicators)
114
+ end
115
+ end
116
+
117
+ # Gets all indicators of answers.
118
+ #
119
+ # @example
120
+ # answers = Answers.new [:abort, :abort_all, :abc]
121
+ # answers.indicators #=> [:a, :A, :a]
122
+ # answers.update_indicators_for_uniqueness!
123
+ # answers.indicators #=> [:a, :A, :ab]
124
+ def indicators
125
+ map(&:indicator)
126
+ end
127
+
128
+ # Returns true if indicators are unique, false otherwise.
129
+ #
130
+ # @example
131
+ # answers = Answers.new [:abort, :abort_all, :abc]
132
+ # answers.indicators #=> [:a, :A, :a]
133
+ # answers.has_unique_indicators? #=> false
134
+ # answers.update_indicators_for_uniqueness!
135
+ # answers.indicators #=> [:a, :A, :ab]
136
+ # answers.has_unique_indicators? #=> true
137
+ def has_unique_indicators?
138
+ indicators == indicators.uniq
139
+ end
140
+
141
+ # Returns all answers as choice string.
142
+ #
143
+ # @example with only true answers
144
+ # answers = Answers.new [:abort, :abort_all, :abc]
145
+ # answers.choice_string #=> "[a]bort, [A]bort all, [ab]c"
146
+ #
147
+ # @example with false answer
148
+ # answers = Answers.new {abort: true, abort_all: false, abc: true}
149
+ # answers.choice_string #=> "[a]bort, [ab]c"
150
+ def to_s
151
+ update_indicators_for_uniqueness!
152
+ map(&:to_s).compact.join(", ")
153
+ end
154
+
155
+ private
156
+
157
+ # {"abc" => true, abort: true, overwrite: false} => {abort: true}
158
+ def select_symbols(args)
159
+ args.select { |inst, value| inst.is_a?(Symbol) }
160
+ end
161
+
162
+ # {abort: true} => [Answer.new(:abort => true)]
163
+ def convert_hash_to_answers(answers)
164
+ answers.reduce([]) { |array, (inst, value)| array << Answer.new({inst => value}) }
165
+ end
166
+
167
+ # :symbol => Answer.new(:symbol)
168
+ # Answer.new(:symbol) => Answer.new(:symbol)
169
+ # "string" => raise ArgumentError
170
+ def convert_symbol_to_answer_or_send_through(arg)
171
+ case arg
172
+ when Answer then arg
173
+ when Symbol then Answer.new(arg)
174
+ else
175
+ raise ArgumentError
176
+ end
177
+ end
178
+
179
+ # Gets first free indicator for `answer` which isn't used by `used_indicators`.
180
+ def free_indicator_of answer, opts={}
181
+ opts = {:used_indicators => []}.merge(opts)
182
+ used_indicators = opts[:used_indicators]
183
+
184
+ i = 1
185
+ loop do
186
+ indicator = answer.indicator(i)
187
+ if used_indicators.include? indicator
188
+ i+=1
189
+ redo # next try
190
+ else
191
+ return indicator
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ module Questions
4
+ # You can ask a question with answers for selection. User can select one answer.
5
+ #
6
+ # @example Long way
7
+ # q = Question.new("File does exist, what should be done?")
8
+ # q.answers = [:skip, :overwrite, :abort]
9
+ # answer = q.ask
10
+ # # SHELL:
11
+ # # $ File does exist, what should be done? [s]kip, [o]verwrite, [a]bort
12
+ # # s<enter>
13
+ # answer #=> :skip
14
+ #
15
+ # @example Short way
16
+ # answer = Question.ask "File does exist, what should be done?", [:skip, :overwrite, :abort]
17
+ # # SHELL:
18
+ # # $ File does exist, what should be done? [s]kip, [o]verwrite, [a]bort
19
+ # # s<enter>
20
+ # answer #=> :skip
21
+ #
22
+ # It's also possible to specify answers as an Hash:
23
+ #
24
+ # @example Hashish way
25
+ # answer = Question.ask "File does exist, what should be done?", skip: true, overwrite: false, abort: true
26
+ # # SHELL:
27
+ # # $ File does exist, what should be done? [s]kip, [a]bort
28
+ # # s<enter>
29
+ # answer #=> :skip
30
+ class Question
31
+ attr_reader :question
32
+ attr_reader :answers
33
+
34
+ # Instantiates a new Question object.
35
+ #
36
+ # @param question [String] This message will be printed
37
+ def initialize(question)
38
+ @question = question
39
+ @answers = Answers.new
40
+ end
41
+
42
+ # Sets answers
43
+ #
44
+ # @example
45
+ # q = Question.new("File does exist, what should be done?")
46
+ # q.answers = [:yes, :no]
47
+ # q.answers.size #=> 2
48
+ # q.answers.map(&:instruction) #=> [:yes, :no]
49
+ #
50
+ def answers=(*args)
51
+ @answers.clear
52
+ args.each { |arg| @answers << arg }
53
+ end
54
+
55
+ # Asks question to user. If user typed wrong indicator, then it will be asked again.
56
+ #
57
+ # @example without answers
58
+ # q = Question.new("What?")
59
+ # q.ask #=> raise Error
60
+ #
61
+ # @example with answers
62
+ # q = Question.new("What?")
63
+ # q.answers = [:yes, :no]
64
+ # q.ask #=> :yes if user typed 'y'
65
+ #
66
+ # @return [Symbol] selected answer
67
+ def ask
68
+ raise "You have to set answers" if answers.empty?
69
+ answer = gets "#{@question} #{answers}"
70
+ answers[answer.to_sym].instruction || ask
71
+ end
72
+
73
+ # Asks question.
74
+ #
75
+ # @param question [String] Message that will be printed
76
+ # @param answers [Array, Hash] Answers that are displayed for selection
77
+ #
78
+ # @example
79
+ # Question.ask("What are you doing?", [:nothing, :cleaning_up])
80
+ # # SHELL:
81
+ # # $ What are you doing? [n]othing, [c]leaning up
82
+ # # n<enter>
83
+ # # => :nothing
84
+ #
85
+ # @example Question with answers as hash.
86
+ # ask("Do you have a problem?", :yes => true, :no => true)
87
+ #
88
+ # @return [Symbol] selected answer
89
+ def self.ask(question, answers)
90
+ question = Question.new(question)
91
+ question.answers = answers
92
+ question.ask
93
+ end
94
+
95
+ private
96
+
97
+ # Prints `msg` and reads user input
98
+ def gets msg
99
+ STDOUT.print("#{msg} ")
100
+ STDOUT.flush
101
+ STDIN.gets.chomp
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module Questions
2
+ VERSION = File.read(File.absolute_path("../../../VERSION", __FILE__))
3
+ end
data/lib/questions.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "questions/version"
2
+ require "questions/answer"
3
+ require "questions/answers"
4
+ require "questions/question"
data/questions.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'questions/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "questions"
8
+ spec.version = Questions::VERSION
9
+ spec.authors = ["DSIW"]
10
+ spec.email = ["dsiw@dsiw-it.de"]
11
+ spec.description = %q{Ask human}
12
+ spec.summary = %q{Ask human}
13
+ spec.homepage = "https://github.com/DSIW/questions"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "version"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "simplecov"
26
+ spec.add_development_dependency "yard"
27
+ spec.add_development_dependency "pry"
28
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe :Answer do
6
+ shared_examples_for 'a true answer' do
7
+ its(:active?) { should be_true }
8
+ its(:inactive?) { should be_false }
9
+ its(:true?) { should be_true }
10
+ its(:false?) { should be_false }
11
+ its(:indicator) { should_not be_nil }
12
+ its(:instruction) { should_not == "" }
13
+ end
14
+
15
+ shared_examples_for 'a true normal answer' do
16
+ it_should_behave_like 'a true answer'
17
+ its(:instruction) { should == :overwrite }
18
+ its(:indicator_hash) { should == {:o => :overwrite} }
19
+ its(:to_s) { should == "[o]verwrite" }
20
+ its(:special?) { should be_false }
21
+
22
+ its(:indicator) { should == :o }
23
+ describe "indicator" do
24
+ it "#indicator(2) == :ov" do
25
+ subject.indicator(2) == :ov
26
+ end
27
+ it "#indicator(3) == :ove" do
28
+ subject.indicator(3) == :ove
29
+ end
30
+ end
31
+ end
32
+
33
+ shared_examples_for 'a true special answer' do
34
+ it_should_behave_like 'a true answer'
35
+ its(:instruction) { should == :overwrite_all }
36
+ its(:indicator_hash) { should == {:O => :overwrite_all} }
37
+ its(:to_s) { should == "[O]verwrite all" }
38
+ its(:special?) { should be_true }
39
+
40
+ its(:indicator) { should == :O }
41
+ describe "indicator" do
42
+ it "#indicator(2) == :OV" do
43
+ subject.indicator(2) == :OV
44
+ end
45
+ it "#indicator(3) == :OVE" do
46
+ subject.indicator(3) == :OVE
47
+ end
48
+ end
49
+ end
50
+
51
+ shared_examples_for 'a false answer' do
52
+ its(:active?) { should be_false }
53
+ its(:inactive?) { should be_true }
54
+ its(:true?) { should be_false }
55
+ its(:false?) { should be_true }
56
+ its(:indicator) { should be_nil }
57
+ its(:instruction) { should == :overwrite }
58
+ its(:indicator_hash) { should be_nil }
59
+ its(:to_s) { should == nil }
60
+ end
61
+
62
+ describe "#initialize" do
63
+ # true normal
64
+ context "with true as value" do
65
+ context "with braces" do
66
+ subject { Answer.new({:overwrite => true}) }
67
+ it_should_behave_like 'a true normal answer'
68
+ end
69
+ context "without braces" do
70
+ subject { Answer.new(:overwrite => true) }
71
+ it_should_behave_like 'a true normal answer'
72
+ end
73
+ end
74
+ context "as symbol" do
75
+ subject { Answer.new(:overwrite) }
76
+ it_should_behave_like 'a true normal answer'
77
+ end
78
+
79
+ # true special
80
+ context "with true as value" do
81
+ context "with braces" do
82
+ subject { Answer.new({:overwrite_all => true}) }
83
+ it_should_behave_like 'a true special answer'
84
+ end
85
+ context "without braces" do
86
+ subject { Answer.new(:overwrite_all => true) }
87
+ it_should_behave_like 'a true special answer'
88
+ end
89
+ end
90
+ context "as symbol" do
91
+ subject { Answer.new(:overwrite_all) }
92
+ it_should_behave_like 'a true special answer'
93
+ end
94
+
95
+ # false
96
+ context "with false as value" do
97
+ context "with braces" do
98
+ subject { Answer.new({:overwrite => false}) }
99
+ it_should_behave_like 'a false answer'
100
+ end
101
+ context "without braces" do
102
+ subject { Answer.new(:overwrite => false) }
103
+ it_should_behave_like 'a false answer'
104
+ end
105
+ end
106
+ context "with nil as value" do
107
+ subject { Answer.new(:overwrite => nil) }
108
+ it_should_behave_like 'a false answer'
109
+ end
110
+ end
111
+
112
+ describe "#indicator=" do
113
+ subject { Answer.new(:overwrite) }
114
+ its(:indicator) { should == :o }
115
+ it "should set indicator" do
116
+ subject.indicator = :ov
117
+ subject.indicator.should == :ov
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,255 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Answers do
6
+ let(:overwrite) { Answer.new(:overwrite) }
7
+ let(:no_overwrite) { Answer.new(:overwrite => false) }
8
+
9
+ let(:abort) { Answer.new(:abort) }
10
+ let(:no_abort) { Answer.new(:abort => false) }
11
+
12
+ let(:answers_array) { [overwrite, abort] }
13
+ let(:answers_array_with_false) { [overwrite, no_abort] }
14
+
15
+ let(:answers_hash) { {overwrite: true, abort: true} }
16
+ let(:answers_hash_with_false) { {overwrite: true, abort: false} }
17
+
18
+ shared_examples_for "one answer" do
19
+ its(:size) { should == 1 }
20
+ its(:first) { should == abort }
21
+ end
22
+
23
+ shared_examples_for "one symbol" do
24
+ its(:size) { should == 1 }
25
+ its(:first) { should be_kind_of Answer }
26
+ its("first.instruction") { should == :abort }
27
+ end
28
+
29
+ shared_examples_for "two answers" do
30
+ its(:size) { should == 2 }
31
+ it "each answer should be kind of Answer" do
32
+ subject.all? { |answer| answer.kind_of? Answer }
33
+ end
34
+ its("first.instruction") { should == :overwrite }
35
+ its("last.instruction") { should == :abort }
36
+ end
37
+
38
+ shared_examples_for "hash with two true items" do
39
+ its(:size) { should == 2 }
40
+ it "each answer should be kind of Answer" do
41
+ subject.all? { |answer| answer.kind_of? Answer }
42
+ end
43
+ it "should be :overwrite and :abort" do
44
+ subject.map(&:instruction).should == [:overwrite, :abort]
45
+ end
46
+ it "each answer should be true" do
47
+ subject.map(&:true?).should == [true, true]
48
+ end
49
+ end
50
+
51
+ shared_examples_for "hash with one true and one false item" do
52
+ its(:size) { should == 2 }
53
+ it "each answer should be kind of Answer" do
54
+ subject.all? { |answer| answer.kind_of? Answer }
55
+ end
56
+ it "should be :overwrite and :abort" do
57
+ subject.map(&:instruction).should == [:overwrite, :abort]
58
+ end
59
+ it "should be true and false" do
60
+ subject.map(&:true?).should == [true, false]
61
+ end
62
+ end
63
+
64
+ describe "#init" do
65
+ context "when parameter is Answer" do
66
+ subject { Answers.new(abort) }
67
+ include_examples "one answer"
68
+ end
69
+ context "when parameter is Symbol" do
70
+ subject { Answers.new(:abort) }
71
+ include_examples "one symbol"
72
+ end
73
+
74
+ context "when parameter is array" do
75
+ subject { Answers.new(answers_array) }
76
+ include_examples "two answers"
77
+ end
78
+ context "when parameter is array filled with symbols" do
79
+ subject { Answers.new([:overwrite, :abort]) }
80
+ include_examples "two answers"
81
+ end
82
+ context "mixed array filled with symbols and answers" do
83
+ subject { Answers.new([:overwrite, abort]) }
84
+ include_examples "two answers"
85
+ end
86
+
87
+ context "when parameter is Hash with true values" do
88
+ subject { Answers.new(answers_hash) }
89
+ include_examples "hash with two true items"
90
+ end
91
+ context "when parameter is Hash with value false" do
92
+ subject { Answers.new(answers_hash_with_false) }
93
+ include_examples "hash with one true and one false item"
94
+ end
95
+ end
96
+
97
+ describe "#<<" do
98
+ subject { Answers.new }
99
+
100
+ context "when parameter is Answer" do
101
+ before { subject << abort }
102
+ include_examples "one answer"
103
+ end
104
+ context "when parameters are Answers with chaining" do
105
+ before { subject << overwrite << abort }
106
+ include_examples "two answers"
107
+ end
108
+ context "when parameter is Symbol" do
109
+ before { subject << :abort }
110
+ include_examples "one symbol"
111
+ end
112
+
113
+ context "when parameter is array" do
114
+ before { subject << answers_array }
115
+ include_examples "two answers"
116
+ end
117
+ context "when parameter is array filled with symbols" do
118
+ before { subject << [:overwrite, :abort] }
119
+ include_examples "two answers"
120
+ end
121
+ context "mixed array filled with symbols and answers" do
122
+ before { subject << [:overwrite, abort] }
123
+ include_examples "two answers"
124
+ end
125
+
126
+ context "when parameter is Hash with true values" do
127
+ before { subject << answers_hash }
128
+ include_examples "hash with two true items"
129
+ end
130
+ context "when parameter is Hash with value false" do
131
+ before { subject << answers_hash_with_false }
132
+ include_examples "hash with one true and one false item"
133
+ end
134
+ end
135
+
136
+ describe "#update_indicators_for_uniqueness!" do
137
+ before { subject << [:abort, :abort_all, :abc] }
138
+ before { subject.send(:update_indicators_for_uniqueness!) }
139
+ let(:indicators) { subject.map(&:indicator) }
140
+ it "indicators should == [:a, :A, :ab]" do
141
+ indicators.should == [:a, :A, :ab]
142
+ end
143
+ it "indicators should only exist unique answers" do
144
+ indicators.should == indicators.uniq
145
+ end
146
+ end
147
+
148
+ describe "#[]" do
149
+ before { subject << [:abort, :abort_all, :abc] }
150
+ it "should select answer by indicator" do
151
+ subject[:a].indicator.should == :a
152
+ subject[:a].instruction.should == :abort
153
+
154
+ subject[:A].indicator.should == :A
155
+ subject[:A].instruction.should == :abort_all
156
+ end
157
+ end
158
+
159
+ describe "#have_unique_indicators?" do
160
+ before { subject << [:abort, :abort_all, :abc] }
161
+ it { should_not have_unique_indicators }
162
+ context "after updating indicators for uniqueness" do
163
+ before { subject.update_indicators_for_uniqueness! }
164
+ it { should have_unique_indicators }
165
+ end
166
+ end
167
+
168
+ describe "#indicators" do
169
+ before { subject << [:abort, :abort_all, :abc] }
170
+ its(:indicators) { should == [:a, :A, :a] }
171
+ context "after updating indicators for uniqueness" do
172
+ before { subject.update_indicators_for_uniqueness! }
173
+ its(:indicators) { should == [:a, :A, :ab] }
174
+ end
175
+ end
176
+
177
+ describe "#to_s" do
178
+ context "all true" do
179
+ before { subject << [:abort, :abort_all, :abc] }
180
+ its(:to_s) { should == "[a]bort, [A]bort all, [ab]c" }
181
+ end
182
+ context "with false value" do
183
+ before { subject << {abort: true, abort_all: false, abc: true} }
184
+ its(:to_s) { should == "[a]bort, [ab]c" }
185
+ end
186
+ end
187
+
188
+ describe "Private:" do
189
+ describe "#select_symbols" do
190
+ let(:hash) { {"abc" => true, abort: true, overwrite: false} }
191
+ it "should return {abort: true, overwrite: false}" do
192
+ subject.send(:select_symbols, hash).should == { abort: true, overwrite: false }
193
+ end
194
+ end
195
+
196
+ describe "#convert_hash_to_answers" do
197
+ let(:hash) { {abort: true, overwrite: false} }
198
+ subject do
199
+ Answers.new.send(:convert_hash_to_answers, hash)
200
+ end
201
+ it "each entry should be kind of Answer" do
202
+ subject.all? { |answer| answer.kind_of? Answer }
203
+ end
204
+ its("first.instruction") { should == :abort }
205
+ its("first.true?") { should be_true }
206
+ its("last.instruction") { should == :overwrite }
207
+ its("last.true?") { should be_false }
208
+ end
209
+
210
+ describe "#convert_symbol_to_answer_or_send_through" do
211
+ subject do
212
+ Answers.new.send(:convert_symbol_to_answer_or_send_through, parameter)
213
+ end
214
+ context "when parameter is a Symbol" do
215
+ let(:parameter) { :abort }
216
+ it { should be_kind_of Answer }
217
+ its(:instruction) { should == :abort }
218
+ end
219
+ context "when parameter is an answer" do
220
+ let(:parameter) { abort }
221
+ it { should == abort }
222
+ end
223
+ context "when parameter is an other object" do
224
+ let(:parameter) { "string" }
225
+ it "should raise an ArgumentError" do
226
+ expect { subject }.to raise_error ArgumentError
227
+ end
228
+ end
229
+ end
230
+
231
+ describe "#free_indicator_of" do
232
+ context ":abc" do
233
+ it "should return :a if it isn't already used" do
234
+ subject.send(:free_indicator_of, abort, :used_indicators => [:b]).should == :a
235
+ end
236
+
237
+ it "should return :a if :used_indicators is empty" do
238
+ subject.send(:free_indicator_of, abort, :used_indicators => []).should == :a
239
+ end
240
+
241
+ it "should return :a if no :used_indicators is set" do
242
+ subject.send(:free_indicator_of, abort).should == :a
243
+ end
244
+
245
+ it "should reuturn :ab if :a is used" do
246
+ subject.send(:free_indicator_of, abort, :used_indicators => [:a]).should == :ab
247
+ end
248
+
249
+ it "should return :ab if :a and :A is used" do
250
+ subject.send(:free_indicator_of, abort, :used_indicators => [:a, :A]).should == :ab
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Question do
6
+ let(:answers_array) { [Answer.new(:overwrite), Answer.new(:abort)] }
7
+
8
+ let(:question_msg) { "What to do?" }
9
+ let(:question) { Question.new(question_msg) }
10
+ subject { question }
11
+
12
+ describe "#init" do
13
+ its(:answers) { should be_kind_of Answers }
14
+ its("answers.size") { should == 0 }
15
+ its(:question) { should == question_msg }
16
+ end
17
+
18
+ describe "#answers=" do
19
+ before { subject.answers = [:abort, :overwrite] }
20
+ its(:answers) { should be_kind_of Answers }
21
+ its("answers.size") { should == 2 }
22
+ end
23
+
24
+ describe "#ask" do
25
+ it "should raise an error without answers" do
26
+ expect { subject.ask }.to raise_error /have to set answers/
27
+ end
28
+
29
+ context "with answers" do
30
+ before { subject.answers = answers_array }
31
+ before do
32
+ subject.should_receive(:gets).with("#{question_msg} [o]verwrite, [a]bort").and_return("o")
33
+ end
34
+ its(:ask) { should == :overwrite }
35
+ end
36
+ end
37
+
38
+ describe "::ask" do
39
+ context "with answers as array" do
40
+ subject { Question.ask(question_msg, [:all, :nothing]) }
41
+ before do
42
+ Question.any_instance.should_receive(:gets).with("#{question_msg} [A]ll, [n]othing").and_return("A")
43
+ end
44
+ it { should == :all }
45
+ end
46
+ context "with answers as hash" do
47
+ subject { Question.ask(question_msg, all: true, nothing: true) }
48
+ before do
49
+ Question.any_instance.should_receive(:gets).with("#{question_msg} [A]ll, [n]othing").and_return("A")
50
+ end
51
+ it { should == :all }
52
+ end
53
+ context "with answers as hash with false value" do
54
+ subject { Question.ask(question_msg, all: true, not: false, nothing: true) }
55
+ before do
56
+ Question.any_instance.should_receive(:gets).with("#{question_msg} [A]ll, [n]othing").and_return("A")
57
+ end
58
+ it { should == :all }
59
+ end
60
+ end
61
+
62
+ describe "Privates:" do
63
+ describe "#gets" do
64
+ before do
65
+ STDOUT.should_receive(:print).with(question_msg + " ")
66
+ STDOUT.should_receive(:flush)
67
+ STDIN.should_receive(:gets).and_return("y\n")
68
+ end
69
+ it { subject.send(:gets, question_msg).should == "y" }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,12 @@
1
+ require 'rspec'
2
+ require 'questions'
3
+
4
+ RSpec.configure do |config|
5
+ include Questions
6
+
7
+ config.mock_with :rspec
8
+
9
+ config.treat_symbols_as_metadata_keys_with_true_values = true
10
+ config.filter_run :focus => true
11
+ config.run_all_when_everything_filtered = true
12
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: questions
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - DSIW
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: version
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Ask human
112
+ email:
113
+ - dsiw@dsiw-it.de
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - .rspec
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - VERSION
125
+ - lib/questions.rb
126
+ - lib/questions/answer.rb
127
+ - lib/questions/answers.rb
128
+ - lib/questions/question.rb
129
+ - lib/questions/version.rb
130
+ - questions.gemspec
131
+ - spec/questions/answer_spec.rb
132
+ - spec/questions/answers_spec.rb
133
+ - spec/questions/question_spec.rb
134
+ - spec/spec_helper.rb
135
+ homepage: https://github.com/DSIW/questions
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.0.0
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Ask human
159
+ test_files:
160
+ - spec/questions/answer_spec.rb
161
+ - spec/questions/answers_spec.rb
162
+ - spec/questions/question_spec.rb
163
+ - spec/spec_helper.rb
164
+ has_rdoc: