jungi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +675 -0
- data/README.md +49 -0
- data/Rakefile +38 -0
- data/bin/jungi +181 -0
- data/lib/jungi/bigfive.rb +329 -0
- data/lib/jungi/classes.rb +193 -0
- data/lib/jungi/cli.rb +105 -0
- data/lib/jungi/oejts.rb +170 -0
- data/lib/jungi/paulhus.rb +130 -0
- metadata +53 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
# JungI - A simple program for taking open source personality tests.
|
2
|
+
# Copyright (C) 2015 Mfrogy Starmade
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# Global Question Module
|
18
|
+
module Question
|
19
|
+
# Question Type Enum
|
20
|
+
module Type
|
21
|
+
YESORNO = :yn
|
22
|
+
SCALE = :scale
|
23
|
+
end
|
24
|
+
|
25
|
+
# Question Answer Module
|
26
|
+
module Answer
|
27
|
+
YES = :yes
|
28
|
+
NO = :no
|
29
|
+
|
30
|
+
# Check value for yes or no value
|
31
|
+
def self.yes_or_no?(value)
|
32
|
+
if value == Question::Answer::YES || value == Question::Answer::NO
|
33
|
+
return true
|
34
|
+
else
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check value for scale value
|
40
|
+
def self.scale?(value)
|
41
|
+
if value == 1 || value == 2 || value == 3 || value == 4 || value == 5
|
42
|
+
return true
|
43
|
+
else
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Reverse scale value
|
49
|
+
def self.reverse_scale(value)
|
50
|
+
unless Question::Answer.scale?(value)
|
51
|
+
fail "#{type} is not a proper scale!"
|
52
|
+
end
|
53
|
+
[5, 4, 3, 2, 1].to_a[value - 1]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks value follows type
|
57
|
+
def self.follows_type?(type, value)
|
58
|
+
if type == Question::Type::YESORNO
|
59
|
+
return Question::Answer.yes_or_no?(value)
|
60
|
+
elsif type == Question::Type::SCALE
|
61
|
+
return Question::Answer.scale?(value)
|
62
|
+
else
|
63
|
+
fail "#{type} is an invalid question type!"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Basic Test Class
|
70
|
+
class Test
|
71
|
+
# The test's questions plus the type of question they are
|
72
|
+
QUESTIONS = [
|
73
|
+
['This is a standard question.', :yn],
|
74
|
+
['This is another question.', :scale]
|
75
|
+
]
|
76
|
+
|
77
|
+
def initialize
|
78
|
+
@answers = []
|
79
|
+
end
|
80
|
+
|
81
|
+
# Enable the use of self.Q<number> for easy implementation of tests
|
82
|
+
def method_missing(name, *args)
|
83
|
+
section = name[1..(name.length - 1)]
|
84
|
+
if name[0] == 'Q' && self.out_of_index?(section.to_i - 1)
|
85
|
+
return @answers[section.to_i - 1]
|
86
|
+
else
|
87
|
+
super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Check for out of index
|
92
|
+
def out_of_index?(index, truism = false)
|
93
|
+
if (index) > (self.class.const_get(:QUESTIONS).length - 1)
|
94
|
+
if truism
|
95
|
+
false
|
96
|
+
else
|
97
|
+
fail "##{index} Out of Index"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
# Get question via index
|
104
|
+
def get_question(index)
|
105
|
+
self.out_of_index? index
|
106
|
+
self.class.const_get(:QUESTIONS)[index].dup
|
107
|
+
end
|
108
|
+
|
109
|
+
# Alias get_question
|
110
|
+
def question(*args)
|
111
|
+
get_question(*args)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Set question answer to value
|
115
|
+
def set_answer(index, value)
|
116
|
+
self.out_of_index? index
|
117
|
+
type = self.class.const_get(:QUESTIONS)[index][1]
|
118
|
+
|
119
|
+
unless Question::Answer.follows_type?(type, value)
|
120
|
+
fail "Type doesn't match with question type!"
|
121
|
+
end
|
122
|
+
@answers[index] = value
|
123
|
+
end
|
124
|
+
|
125
|
+
# Alias set_answer
|
126
|
+
def answer(*args)
|
127
|
+
set_answer(*args)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Alias set_answer
|
131
|
+
def answer=(args)
|
132
|
+
set_answer(*args)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get a list of the answers so far
|
136
|
+
def answers
|
137
|
+
Marshal.load(Marshal.dump(@answers))
|
138
|
+
end
|
139
|
+
|
140
|
+
# Done supplying answers to questions?
|
141
|
+
def finished?
|
142
|
+
if @answers.length >= self.class.const_get(:QUESTIONS).length
|
143
|
+
return true
|
144
|
+
else
|
145
|
+
return false
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Alias finished?
|
150
|
+
def done?
|
151
|
+
self.finished?
|
152
|
+
end
|
153
|
+
|
154
|
+
# Stub method for result of test
|
155
|
+
def result
|
156
|
+
fail 'Not ready yet!' unless self.finished?
|
157
|
+
@answers.to_s
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Basic Scale5 Test Class
|
162
|
+
class ScaleTest < Test
|
163
|
+
# Default scale questions
|
164
|
+
QUESTIONS = ['very not dead|very dead', 'very not alive|very alive']
|
165
|
+
|
166
|
+
# Set question answer to value
|
167
|
+
def set_answer(index, value)
|
168
|
+
self.out_of_index? index
|
169
|
+
fail "#{value} is not a scale!" unless Question::Answer.scale?(value)
|
170
|
+
@answers[index] = value
|
171
|
+
end
|
172
|
+
|
173
|
+
# Randomize the answers to a scale test
|
174
|
+
def randomize!
|
175
|
+
self.class.const_get(:QUESTIONS).length.times do |num|
|
176
|
+
set_answer(num, rand(1..5))
|
177
|
+
end
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
181
|
+
# Default results for scale test
|
182
|
+
def result
|
183
|
+
fail 'Not ready yet!' unless self.finished?
|
184
|
+
val = @answers[1] - @answers[0]
|
185
|
+
if val < 0
|
186
|
+
"Sad to hear you're dead."
|
187
|
+
elsif val > 0
|
188
|
+
"Good to hear you're alive!"
|
189
|
+
else
|
190
|
+
"You're hard to figure out."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
data/lib/jungi/cli.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# JungI - A simple program for taking open source personality tests.
|
2
|
+
# Copyright (C) 2015 Mfrogy Starmade
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
begin
|
18
|
+
require_relative './classes'
|
19
|
+
rescue LoadError
|
20
|
+
require 'jungi/classes'
|
21
|
+
end
|
22
|
+
|
23
|
+
# JungiCli utility module
|
24
|
+
module JungiCli
|
25
|
+
# Fetch terminal width
|
26
|
+
def self.width
|
27
|
+
val = `tput cols`.chomp.to_i
|
28
|
+
val = 80 if val == 0
|
29
|
+
val
|
30
|
+
rescue Errno::ENOENT
|
31
|
+
80
|
32
|
+
end
|
33
|
+
|
34
|
+
# Fetch prototype objects
|
35
|
+
def self.scale_proto
|
36
|
+
start = (' |1|2|3|4|5| '.center width)
|
37
|
+
start.partition(' |1|2|3|4|5| ')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Handle question via scale
|
41
|
+
def self.parse_scale(scale)
|
42
|
+
p1, p2 = scale.split '|'
|
43
|
+
if p2
|
44
|
+
s1, c1, s2 = scale_proto
|
45
|
+
s1[s1.length - p1.length, s1.length - 1] = p1
|
46
|
+
s2[0, p2.length - 1] = p2
|
47
|
+
s1 + c1 + s2
|
48
|
+
else
|
49
|
+
(p1 + ' |1|2|3|4|5| ').center WIDTH
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Ask for integer
|
54
|
+
def self.ask_integer(prompt)
|
55
|
+
result = nil
|
56
|
+
until yield result
|
57
|
+
print prompt
|
58
|
+
result = gets.chomp.to_i
|
59
|
+
end
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# Nicely ask a scale
|
64
|
+
def self.ask_scale(scale, randomize = false)
|
65
|
+
puts parse_scale scale
|
66
|
+
|
67
|
+
if randomize
|
68
|
+
int = rand(1..5)
|
69
|
+
puts "> #{int}"
|
70
|
+
int
|
71
|
+
|
72
|
+
else
|
73
|
+
ask_integer '> ' do |r|
|
74
|
+
Question::Answer.scale? r
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Pad a document to width
|
80
|
+
def self.center_doc(doc)
|
81
|
+
doc = doc.split "\n"
|
82
|
+
out = []
|
83
|
+
doc.length.times do |int|
|
84
|
+
out[int] = doc[int].center width
|
85
|
+
end
|
86
|
+
out
|
87
|
+
end
|
88
|
+
|
89
|
+
# Display document centered
|
90
|
+
def self.display_doc(doc)
|
91
|
+
center_doc(doc).each do |line|
|
92
|
+
puts line
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Render line
|
97
|
+
def self.line
|
98
|
+
puts '-' * width
|
99
|
+
end
|
100
|
+
|
101
|
+
# Clear screen
|
102
|
+
def self.clr
|
103
|
+
system('clear') || system('cls')
|
104
|
+
end
|
105
|
+
end
|
data/lib/jungi/oejts.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
# JungI - A simple program for taking open source personality tests.
|
2
|
+
# Copyright (C) 2015 Mfrogy Starmade
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
# The items of the Open Extended Jungian Type Scales 1.2 are licenced
|
18
|
+
# under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0
|
19
|
+
# International License by Eric Jorgenson. The OEJTS comes with no
|
20
|
+
# guarantees of reliability or accuracy of any kind.
|
21
|
+
#
|
22
|
+
# https://creativecommons.org/licenses/by-nc-sa/4.0/
|
23
|
+
|
24
|
+
begin
|
25
|
+
require_relative './classes'
|
26
|
+
rescue LoadError
|
27
|
+
require 'jungi/classes'
|
28
|
+
end
|
29
|
+
|
30
|
+
# Implementation of OEJTS
|
31
|
+
class OEJTSTest < ScaleTest
|
32
|
+
DOC = <<END_FILE
|
33
|
+
Open Extended Jungian Type Scales 1.2
|
34
|
+
|
35
|
+
In this test there are 32 pairs of personality descriptions
|
36
|
+
connected by a five point scale. For each pair, you must
|
37
|
+
choose where on the scale between them you think you are.
|
38
|
+
For example, if the pair is "angry” versus “calm”,
|
39
|
+
you should type in a 1 if you are always angry and never
|
40
|
+
calm, a 3 if you are half and half, etc. Press <ENTER>
|
41
|
+
after typing your answer. Answer honestly and as you are,
|
42
|
+
not as you hope to be.
|
43
|
+
END_FILE
|
44
|
+
QUESTIONS = [
|
45
|
+
'makes lists|relies on memory',
|
46
|
+
'sceptical|wants to believe',
|
47
|
+
'bored by time alone|needs time alone',
|
48
|
+
'accepts things as they are|unsatisfied with the way things are',
|
49
|
+
'keeps a clean room|just puts stuff where ever',
|
50
|
+
"thinks \"robotic\" is an insult|strives to have a mechanical mind",
|
51
|
+
'energetic|mellow',
|
52
|
+
'prefer to take multiple choice test|prefer essay answers',
|
53
|
+
'chaotic|organized',
|
54
|
+
'easily hurt|thick-skinned',
|
55
|
+
'works best in groups|works best alone',
|
56
|
+
'focused on the past|focused on the future',
|
57
|
+
'plans far ahead|plans at the last minute',
|
58
|
+
"wants people's respect|wants their love",
|
59
|
+
'gets worn out by parties|gets fired up by parties',
|
60
|
+
'fits in|stands out',
|
61
|
+
'keeps options open|commits',
|
62
|
+
'wants to be good at fixing things|wants to be good at fixing people',
|
63
|
+
'talks more|listens more',
|
64
|
+
'when describing an event, will tell people what happened|when descr'\
|
65
|
+
'ibing an event, will tell people what it meant',
|
66
|
+
'gets work done right away|procrastinates',
|
67
|
+
'follows the heart|follows the head',
|
68
|
+
'stays at home|goest out on the town',
|
69
|
+
'wants the big picture|wants the details',
|
70
|
+
'improvises|prepares',
|
71
|
+
'bases morality on justice|bases morality on compassion',
|
72
|
+
'finds it difficult to yell very loudly|yelling to others when they are'\
|
73
|
+
' far away comes naturally',
|
74
|
+
'theoretical|empirical',
|
75
|
+
'works hard|plays hard',
|
76
|
+
'uncomfortable with emotions|values emotions',
|
77
|
+
'likes to perform in front of other people|avoids public speaking',
|
78
|
+
"likes to know \"who?\", \"what?\", \"when?\"|likes to know \"why?\""
|
79
|
+
]
|
80
|
+
|
81
|
+
def result
|
82
|
+
fail 'Not ready yet!' unless self.finished?
|
83
|
+
ie = 30 - self.Q3 - self.Q7 - self.Q11 + self.Q15 -
|
84
|
+
self.Q19 + self.Q23 + self.Q27 - self.Q31
|
85
|
+
sn = 12 + self.Q4 + self.Q8 + self.Q12 + self.Q16 +
|
86
|
+
self.Q20 - self.Q24 - self.Q28 + self.Q32
|
87
|
+
ft = 30 - self.Q2 + self.Q6 + self.Q10 - self.Q14 -
|
88
|
+
self.Q18 + self.Q22 - self.Q26 - self.Q30
|
89
|
+
jp = 18 + self.Q1 + self.Q5 - self.Q9 + self.Q13 -
|
90
|
+
self.Q17 + self.Q21 - self.Q25 + self.Q29
|
91
|
+
desig = ''
|
92
|
+
|
93
|
+
if ie > 24
|
94
|
+
desig << 'E'
|
95
|
+
else
|
96
|
+
desig << 'I'
|
97
|
+
end
|
98
|
+
if sn > 24
|
99
|
+
desig << 'N'
|
100
|
+
else
|
101
|
+
desig << 'S'
|
102
|
+
end
|
103
|
+
if ft > 24
|
104
|
+
desig << 'T'
|
105
|
+
else
|
106
|
+
desig << 'F'
|
107
|
+
end
|
108
|
+
if jp > 24
|
109
|
+
desig << 'P'
|
110
|
+
else
|
111
|
+
desig << 'J'
|
112
|
+
end
|
113
|
+
|
114
|
+
iev = (ie - 24).abs
|
115
|
+
snv = (sn - 24).abs
|
116
|
+
ftv = (ft - 24).abs
|
117
|
+
jpv = (jp - 24).abs
|
118
|
+
|
119
|
+
[desig, iev, snv, ftv, jpv]
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.parse_iev(desig, iev)
|
123
|
+
word = desig[0] == 'E' ? 'Extraversion' : 'Intraversion'
|
124
|
+
if iev.between?(0, 1)
|
125
|
+
"Lukewarm #{word}, "
|
126
|
+
elsif iev < 4
|
127
|
+
"Weak(#{iev}) #{word}, "
|
128
|
+
else
|
129
|
+
"Strong(#{iev}) #{word}, "
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.parse_snv(desig, snv)
|
134
|
+
word = desig[1] == 'S' ? 'Sensing' : 'Intuition'
|
135
|
+
if snv.between?(0, 1)
|
136
|
+
"Lukewarm #{word}\n"
|
137
|
+
elsif snv < 4
|
138
|
+
"Weak(#{snv}) #{word}\n"
|
139
|
+
else
|
140
|
+
"Strong(#{snv}) #{word}\n"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.parse_ftv(desig, ftv)
|
145
|
+
word = desig[2] == 'F' ? 'Feeling' : 'Thinking'
|
146
|
+
if ftv.between?(0, 1)
|
147
|
+
"Lukewarm #{word}, "
|
148
|
+
elsif ftv < 4
|
149
|
+
"Weak(#{ftv}) #{word} "
|
150
|
+
else
|
151
|
+
"Strong(#{ftv}) #{word}, "
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.parse_jpv(desig, jpv)
|
156
|
+
word = desig[3] == 'J' ? 'Judging' : 'Perceiving'
|
157
|
+
if jpv.between?(0, 1)
|
158
|
+
"Lukewarm #{word}"
|
159
|
+
elsif jpv < 4
|
160
|
+
"Weak(#{jpv}) #{word}"
|
161
|
+
else
|
162
|
+
"Strong(#{jpv}) #{word}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.parse_result(desig, iev, snv, ftv, jpv)
|
167
|
+
parse_iev(desig, iev) << parse_snv(desig, snv) <<
|
168
|
+
parse_ftv(desig, ftv) << parse_jpv(desig, jpv)
|
169
|
+
end
|
170
|
+
end
|