surveyor 0.6.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.
- data/.gitignore +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +104 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/app/controllers/surveyor_controller.rb +117 -0
- data/app/helpers/survey_form_builder.rb +37 -0
- data/app/helpers/surveyor_helper.rb +66 -0
- data/app/models/answer.rb +21 -0
- data/app/models/dependency.rb +46 -0
- data/app/models/dependency_condition.rb +79 -0
- data/app/models/question.rb +54 -0
- data/app/models/question_group.rb +18 -0
- data/app/models/response.rb +47 -0
- data/app/models/response_set.rb +185 -0
- data/app/models/survey.rb +57 -0
- data/app/models/survey_section.rb +15 -0
- data/app/views/layouts/surveyor_default.html.erb +13 -0
- data/app/views/partials/_answer.html.haml +49 -0
- data/app/views/partials/_question.html.haml +73 -0
- data/app/views/partials/_question_group.html.haml +41 -0
- data/app/views/surveyor/edit.html.haml +32 -0
- data/app/views/surveyor/new.html.haml +17 -0
- data/app/views/surveyor/show.html.haml +12 -0
- data/config/routes.rb +10 -0
- data/generators/surveyor/EXTENDING_SURVEYOR +12 -0
- data/generators/surveyor/extend_surveyor_generator.rb +22 -0
- data/generators/surveyor/surveyor_generator.rb +60 -0
- data/generators/surveyor/templates/README +10 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_resize_se.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_35x9_colorpicker_indicator.gif.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/dadada_40x100_textures_02_glass_75.png +0 -0
- data/generators/surveyor/templates/assets/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
- data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_01_flat_0.png +0 -0
- data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_02_glass_65.png +0 -0
- data/generators/surveyor/templates/assets/javascripts/accessibleUISlider.jQuery.js +201 -0
- data/generators/surveyor/templates/assets/javascripts/jquery-1.2.6.js +3549 -0
- data/generators/surveyor/templates/assets/javascripts/jquery-ui-personalized-1.5.3.js +7616 -0
- data/generators/surveyor/templates/assets/javascripts/jquery.form.js +637 -0
- data/generators/surveyor/templates/assets/javascripts/surveyor.js +28 -0
- data/generators/surveyor/templates/assets/stylesheets/jquery-ui-slider-additions.css +71 -0
- data/generators/surveyor/templates/assets/stylesheets/reset.css +46 -0
- data/generators/surveyor/templates/assets/stylesheets/sass/surveyor.sass +245 -0
- data/generators/surveyor/templates/assets/stylesheets/surveyor.css +231 -0
- data/generators/surveyor/templates/assets/stylesheets/ui.theme.css +851 -0
- data/generators/surveyor/templates/extensions/survey_extensions.rb +18 -0
- data/generators/surveyor/templates/extensions/surveyor_controller_extensions.rb +28 -0
- data/generators/surveyor/templates/extensions/surveyor_custom.html.erb +13 -0
- data/generators/surveyor/templates/extensions/surveyor_helper_extensions.rb +18 -0
- data/generators/surveyor/templates/initializers/haml.rb +8 -0
- data/generators/surveyor/templates/initializers/surveyor.rb +11 -0
- data/generators/surveyor/templates/migrate/create_answers.rb +45 -0
- data/generators/surveyor/templates/migrate/create_dependencies.rb +21 -0
- data/generators/surveyor/templates/migrate/create_dependency_conditions.rb +29 -0
- data/generators/surveyor/templates/migrate/create_question_groups.rb +18 -0
- data/generators/surveyor/templates/migrate/create_questions.rb +33 -0
- data/generators/surveyor/templates/migrate/create_response_sets.rb +22 -0
- data/generators/surveyor/templates/migrate/create_responses.rb +33 -0
- data/generators/surveyor/templates/migrate/create_survey_sections.rb +25 -0
- data/generators/surveyor/templates/migrate/create_surveys.rb +25 -0
- data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +201 -0
- data/generators/surveyor/templates/tasks/surveyor.rb +4 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/surveyor.rb +7 -0
- data/lib/surveyor/config.rb +45 -0
- data/lib/tasks/surveyor_tasks.rake +33 -0
- data/lib/tiny_code.rb +58 -0
- data/lib/xml_formatter.rb +12 -0
- data/script/surveyor/answer.rb +84 -0
- data/script/surveyor/columnizer.rb +36 -0
- data/script/surveyor/dependency.rb +43 -0
- data/script/surveyor/dependency_condition.rb +74 -0
- data/script/surveyor/question.rb +76 -0
- data/script/surveyor/question_group.rb +33 -0
- data/script/surveyor/specs/answer_spec.rb +66 -0
- data/script/surveyor/specs/question_dependency_spec.rb +46 -0
- data/script/surveyor/specs/question_group_spec.rb +9 -0
- data/script/surveyor/specs/question_spec.rb +111 -0
- data/script/surveyor/specs/section_spec.rb +58 -0
- data/script/surveyor/survey.rb +108 -0
- data/script/surveyor/survey_parser.rb +64 -0
- data/script/surveyor/survey_section.rb +153 -0
- data/script/surveyor/whr_dsl.tmproj +244 -0
- data/spec/controllers/surveyor_controller_spec.rb +328 -0
- data/spec/factories.rb +107 -0
- data/spec/models/answer_spec.rb +29 -0
- data/spec/models/dependency_condition_spec.rb +338 -0
- data/spec/models/dependency_spec.rb +82 -0
- data/spec/models/question_group_spec.rb +11 -0
- data/spec/models/question_spec.rb +75 -0
- data/spec/models/response_set_spec.rb +153 -0
- data/spec/models/response_spec.rb +94 -0
- data/spec/models/survey_section_spec.rb +34 -0
- data/spec/models/survey_spec.rb +72 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +14 -0
- data/surveyor.gemspec +187 -0
- data/uninstall.rb +1 -0
- metadata +211 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class QuestionGroup
|
|
2
|
+
|
|
3
|
+
# Context, Content, Display
|
|
4
|
+
attr_accessor :id, :section_id, :section, :parser
|
|
5
|
+
attr_accessor :text, :help_text
|
|
6
|
+
attr_accessor :display_type
|
|
7
|
+
|
|
8
|
+
# id, section and text required
|
|
9
|
+
def initialize(section, args, options)
|
|
10
|
+
self.parser = section.parser
|
|
11
|
+
self.id = parser.new_question_group_id
|
|
12
|
+
self.text = args[0]
|
|
13
|
+
self.default_options().merge(options).merge(args[1] || {}).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def default_options()
|
|
17
|
+
{:display_type => "default"}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_yml
|
|
21
|
+
out =[ %(#{@id}:) ]
|
|
22
|
+
out << %( id: #{@id})
|
|
23
|
+
out << %( text: "#{@text}")
|
|
24
|
+
out << %( help_text: "#{@help_text}")
|
|
25
|
+
out << %( display_type: "#{@display_type}")
|
|
26
|
+
(out << nil ).join("\r\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_file
|
|
30
|
+
File.open(self.parser.question_groups_yml, File::CREAT|File::APPEND|File::WRONLY){ |f| f << to_yml }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../answer'
|
|
2
|
+
|
|
3
|
+
describe Answer, " when first created" do
|
|
4
|
+
|
|
5
|
+
TEST_ID = 1
|
|
6
|
+
TEST_CONTEXT_ID = "b3a_1"
|
|
7
|
+
TEST_QUESTION_ID = "2"
|
|
8
|
+
TEST_TEXT = "No / Rarely"
|
|
9
|
+
TEST_OPTIONS = {:help_text => "Never or rarely ever"}
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
@ans = Answer.new(TEST_ID, TEST_QUESTION_ID, TEST_CONTEXT_ID, TEST_TEXT, TEST_OPTIONS)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should set inititalized variables to those passed in" do
|
|
16
|
+
@ans.id.should == TEST_ID
|
|
17
|
+
@ans.question_id.should == TEST_QUESTION_ID
|
|
18
|
+
@ans.context_id.should == TEST_CONTEXT_ID
|
|
19
|
+
@ans.text.should == TEST_TEXT
|
|
20
|
+
@ans.help_text.should == TEST_OPTIONS[:help_text]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should output current state to yml" do
|
|
24
|
+
@ans.should.respond_to?(:to_yml)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should create a normalized code from the answer text" do
|
|
28
|
+
# The answer object should take the title of the text and convert
|
|
29
|
+
# it to a code that is more appropirate for a database entry
|
|
30
|
+
|
|
31
|
+
# Taking a few answers from the survey for testing
|
|
32
|
+
str = []
|
|
33
|
+
str[0] = "This? is a in - t3rrible-@nswer of! (question) on"
|
|
34
|
+
str[1] = "Private insurance/ HMO/ PPO"
|
|
35
|
+
str[2] = "VA"
|
|
36
|
+
str[3] = "PMS (Premenstrual syndrome)/ PMDD (Premenstrual Dysphoric Disorder)"
|
|
37
|
+
str[4] = "Have never been employed outside the home"
|
|
38
|
+
str[5] = "Professional"
|
|
39
|
+
str[6] = "Not working because of temporary disability, but expect to return to a job"
|
|
40
|
+
|
|
41
|
+
# What the results should look like
|
|
42
|
+
r_str = []
|
|
43
|
+
r_str[0] = "this_t3rrible_nswer"
|
|
44
|
+
r_str[1] = "private_insurance_hmo_ppo"
|
|
45
|
+
r_str[2] = "va"
|
|
46
|
+
r_str[3] = "pms_pmdd"
|
|
47
|
+
r_str[4] = "never_been_employed_outside_home"
|
|
48
|
+
r_str[5] = "professional"
|
|
49
|
+
r_str[6] = "temporary_disability_expect_return_job"
|
|
50
|
+
|
|
51
|
+
count = 0
|
|
52
|
+
str.each do |s|
|
|
53
|
+
|
|
54
|
+
code = Answer.to_normalized_code(s)
|
|
55
|
+
code.should eql(r_str[count])
|
|
56
|
+
count += 1
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should create a normalized code automatically when initalized" do
|
|
63
|
+
@ans.code.should eql("no_rarely")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../question_dependency'
|
|
2
|
+
|
|
3
|
+
describe QuestionDependency, " when first created" do
|
|
4
|
+
|
|
5
|
+
ID = 1
|
|
6
|
+
QUESTION_ID = 2
|
|
7
|
+
DEPENDENT_QUESTION_ID = 3
|
|
8
|
+
DEPENDENT_GROUP_ID = nil
|
|
9
|
+
DEPENDENCY_TYPE_1 = {:dependency_type => :response_count, :conditional => :one_or_more }
|
|
10
|
+
DEPENDENCY_TYPE_2 = {:dependency_type => :response_answer, :conditional => :selected, :answer_id => 1}
|
|
11
|
+
DEPENDENCY_TYPE_3 = {:dependency_type => :response_value, :condition => :not_zero}
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
@dependent_1 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_1)
|
|
15
|
+
@dependent_2 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_2)
|
|
16
|
+
@dependent_3 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_3)
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should set initialization paramerters properly" do
|
|
21
|
+
# Checking both type1 and type2 dependencies
|
|
22
|
+
@dependent_1.id.should == ID
|
|
23
|
+
@dependent_1.question_id.should == QUESTION_ID
|
|
24
|
+
@dependent_1.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
25
|
+
@dependent_1.dependency_type.should == DEPENDENCY_TYPE_1[:dependency_type]
|
|
26
|
+
@dependent_1.conditional.should == DEPENDENCY_TYPE_1[:conditional]
|
|
27
|
+
|
|
28
|
+
@dependent_2.id.should == ID
|
|
29
|
+
@dependent_2.question_id.should == QUESTION_ID
|
|
30
|
+
@dependent_2.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
31
|
+
@dependent_2.dependency_type.should == DEPENDENCY_TYPE_2[:dependency_type]
|
|
32
|
+
@dependent_2.conditional.should == DEPENDENCY_TYPE_2[:conditional]
|
|
33
|
+
@dependent_2.answer_id.should == DEPENDENCY_TYPE_2[:answer_id]
|
|
34
|
+
|
|
35
|
+
@dependent_3.id.should == ID
|
|
36
|
+
@dependent_3.question_id.should == QUESTION_ID
|
|
37
|
+
@dependent_3.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
38
|
+
@dependent_3.dependency_type.should == DEPENDENCY_TYPE_3[:dependency_type]
|
|
39
|
+
@dependent_3.conditional.should == DEPENDENCY_TYPE_3[:conditional]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should output current state to yml" do
|
|
43
|
+
@ans.should.respond_to?(:to_yml)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../question'
|
|
2
|
+
|
|
3
|
+
describe Question, " when first created" do
|
|
4
|
+
TEST_ID = 1
|
|
5
|
+
TEST_CONTEXT_ID = "B3"
|
|
6
|
+
TEST_SECTION_ID = 2
|
|
7
|
+
TEST_TEXT = "In the past 12 months how many times have you been to a doctor?"
|
|
8
|
+
TEST_HELP_TEXT = "Please give a rough estimate"
|
|
9
|
+
TEST_OPTIONS = {:help_text => TEST_HELP_TEXT }
|
|
10
|
+
TEST_CODE = "many_times_you_been_doctor"
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
@ques = Question.new(TEST_ID, TEST_SECTION_ID, TEST_CONTEXT_ID, TEST_TEXT, TEST_OPTIONS)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should set initialization parameters properly" do
|
|
17
|
+
@ques.id.should == TEST_ID
|
|
18
|
+
@ques.context_id.should == TEST_CONTEXT_ID
|
|
19
|
+
@ques.section_id.should == TEST_SECTION_ID
|
|
20
|
+
@ques.code.should == TEST_CODE
|
|
21
|
+
@ques.options[:help_text].should == TEST_HELP_TEXT
|
|
22
|
+
|
|
23
|
+
#Checking the defaults
|
|
24
|
+
@ques.options[:has_omit?].should == true
|
|
25
|
+
@ques.options[:has_other?].should == false
|
|
26
|
+
@ques.options[:answer_format].should == :medium_text
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should output current state to yml" do
|
|
30
|
+
@ques.should.respond_to?(:to_yml)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should create a normalized code from the answer text" do
|
|
34
|
+
# The question object should take the title of the text and convert
|
|
35
|
+
# it to a code that is more appropirate for a database entry
|
|
36
|
+
|
|
37
|
+
# Taking a few questions from the survey for testing
|
|
38
|
+
str = []
|
|
39
|
+
str[0] = "How long has it been since you last visited a doctor for a routine checkup (routine being not for a particular reason)?"
|
|
40
|
+
str[1] = "Do you take medications as directed?"
|
|
41
|
+
str[2] = "Do you every leak urine (or) water when you didn't want to?" #checking for () and ' removal
|
|
42
|
+
str[3] = "Do your biological family members (not adopted) have a \"history\" of any of the following?"
|
|
43
|
+
str[4] = "Your health:"
|
|
44
|
+
str[5] = "In general, you would say your health is:"
|
|
45
|
+
|
|
46
|
+
# What the results should look like
|
|
47
|
+
r_str = []
|
|
48
|
+
r_str[0] = "visited_doctor_for_routine_checkup"
|
|
49
|
+
r_str[1] = "you_take_medications_as_directed"
|
|
50
|
+
r_str[2] = "urine_water_you_didnt_want"
|
|
51
|
+
r_str[3] = "family_members_history_any_following"
|
|
52
|
+
r_str[4] = "your_health"
|
|
53
|
+
r_str[5] = "you_would_say_your_health"
|
|
54
|
+
|
|
55
|
+
count = 0
|
|
56
|
+
str.each do |s|
|
|
57
|
+
|
|
58
|
+
code = Question.to_normalized_code(s)
|
|
59
|
+
code.should eql(r_str[count])
|
|
60
|
+
count += 1
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should create a normalized code automatically when initalized" do
|
|
67
|
+
@ques.code.should eql(TEST_CODE)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should update the normalized code if the title is changed" do
|
|
71
|
+
@ques.code.should eql(TEST_CODE)
|
|
72
|
+
@ques.text = "Sometimes or All the time?"
|
|
73
|
+
@ques.code.should eql("sometimes_all_time")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe Question, " when it contains data" do
|
|
79
|
+
|
|
80
|
+
before do # Mocking up some answers
|
|
81
|
+
@question = Question.new(1,TEST_SECTION_ID,TEST_CONTEXT_ID,TEST_TEXT,TEST_OPTIONS)
|
|
82
|
+
ma1 = mock("answer")
|
|
83
|
+
ma1.stub!(:context_id).and_return("1")
|
|
84
|
+
@question.add_answer(ma1)
|
|
85
|
+
|
|
86
|
+
ma2 = mock("answer")
|
|
87
|
+
ma2.stub!(:context_id).and_return("2")
|
|
88
|
+
@question.add_answer(ma2)
|
|
89
|
+
|
|
90
|
+
ma3 = mock("answer")
|
|
91
|
+
ma3.stub!(:context_id).and_return("3")
|
|
92
|
+
@question.add_answer(ma3)
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should have added the test answers correctly" do
|
|
97
|
+
@question.answers.length.should eql(3)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should have a question text" do
|
|
101
|
+
@question.text.should eql(TEST_TEXT)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should find an answer by context_id" do
|
|
105
|
+
pending # yoon: commented out during dsl refactoring
|
|
106
|
+
a_to_find = @question.find_answer_by_context_id("2")
|
|
107
|
+
a_to_find.should_not eql(nil)
|
|
108
|
+
a_to_find.context_id.should eql("2")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../section'
|
|
2
|
+
|
|
3
|
+
describe Section, " when first created" do
|
|
4
|
+
|
|
5
|
+
TEST_TITLE = "Demographics"
|
|
6
|
+
TEST_SECTION = :B
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
@section = Section.new(1,TEST_SECTION,TEST_TITLE)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should accept questions" do
|
|
13
|
+
mock_question = mock("question")
|
|
14
|
+
@section.questions.size.should eql(0)
|
|
15
|
+
@section.add_question(mock_question)
|
|
16
|
+
@section.questions.size.should eql(1)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should output current state to code" do
|
|
20
|
+
@section.should.respond_to?(:to_code)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe Section, " when it contains data" do
|
|
26
|
+
|
|
27
|
+
before do # Mocking up some questions
|
|
28
|
+
@section = Section.new(1,TEST_SECTION,TEST_TITLE)
|
|
29
|
+
mq1 = mock("question")
|
|
30
|
+
mq1.stub!(:context_id).and_return("B1")
|
|
31
|
+
@section.add_question(mq1)
|
|
32
|
+
|
|
33
|
+
mq2 = mock("question")
|
|
34
|
+
mq2.stub!(:context_id).and_return("B2")
|
|
35
|
+
@section.add_question(mq2)
|
|
36
|
+
|
|
37
|
+
mq3 = mock("question")
|
|
38
|
+
mq3.stub!(:context_id).and_return("B3")
|
|
39
|
+
@section.add_question(mq3)
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should have added the test questions correctly" do
|
|
44
|
+
@section.questions.length.should eql(3)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should have a title" do
|
|
48
|
+
@section.title.should eql(TEST_TITLE)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should find a question by context_id" do
|
|
52
|
+
pending # yoon: commented out during dsl refactoring
|
|
53
|
+
q_to_find = @section.find_question_by_context_id("B2")
|
|
54
|
+
q_to_find.should_not eql(nil)
|
|
55
|
+
q_to_find.context_id.should eql("B2")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/survey_section'
|
|
2
|
+
|
|
3
|
+
class Survey
|
|
4
|
+
include Columnizer
|
|
5
|
+
# Content, Reference, Expiry, Children
|
|
6
|
+
attr_accessor :id, :parser
|
|
7
|
+
attr_accessor :title, :description
|
|
8
|
+
attr_accessor :access_code
|
|
9
|
+
attr_accessor :active_at, :inactive_at
|
|
10
|
+
attr_accessor :survey_sections
|
|
11
|
+
|
|
12
|
+
@@current_survey = nil
|
|
13
|
+
|
|
14
|
+
def self.current_survey
|
|
15
|
+
@@current_survey
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.current_survey=(value)
|
|
19
|
+
@@current_survey = value
|
|
20
|
+
puts "Assigning current survey #{@@current_survey.title}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# id, parser, and title required
|
|
24
|
+
def initialize(id, parser, title, options = {})
|
|
25
|
+
self.id = id
|
|
26
|
+
self.parser = parser
|
|
27
|
+
self.title = title
|
|
28
|
+
self.survey_sections = []
|
|
29
|
+
self.access_code = Columnizer.to_normalized_column(title)
|
|
30
|
+
# self.default_options(title).merge(options).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
|
31
|
+
Survey.current_survey = self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# def default_options(title)
|
|
35
|
+
# {}
|
|
36
|
+
# end
|
|
37
|
+
|
|
38
|
+
# This method_missing magic does all the heavy lifting for the DSL
|
|
39
|
+
def method_missing(missing_method, *args, &block)
|
|
40
|
+
m_name = missing_method.to_s
|
|
41
|
+
if (section_match = m_name.match(/(\Asection)_?(.*)/))
|
|
42
|
+
build_survey_section(section_match[3].to_s, *args, &block) # Parse "section" method, create new section in this survey
|
|
43
|
+
else
|
|
44
|
+
puts " ERROR: '#{m_name}' not valid method_missing name"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_survey_section(reference_identifier, title, &block)
|
|
49
|
+
puts " Section: #{title}"
|
|
50
|
+
if block_given?
|
|
51
|
+
new_survey_section = SurveySection.new(new_survey_section_id, self, title, {:display_order => self.survey_sections.size + 1, :reference_identifier => reference_identifier})
|
|
52
|
+
new_survey_section.instance_eval(&block)
|
|
53
|
+
add_survey_section(new_survey_section)
|
|
54
|
+
puts " Section added"
|
|
55
|
+
else
|
|
56
|
+
puts " ERROR: A section cannot be empty!"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def new_survey_section_id
|
|
61
|
+
self.parser.new_survey_section_id
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_survey_section(survey_section)
|
|
65
|
+
self.survey_sections << survey_section
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Used to find questions for dependency linking
|
|
69
|
+
def find_question_by_reference(ref_id)
|
|
70
|
+
question = nil
|
|
71
|
+
count = 0
|
|
72
|
+
while question.nil? and count < self.survey_sections.size
|
|
73
|
+
question = self.survey_sections[count].find_question_by_reference(ref_id)
|
|
74
|
+
count += 1
|
|
75
|
+
end
|
|
76
|
+
question
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def reconcile_dependencies
|
|
81
|
+
|
|
82
|
+
@survey_sections.each do |section|
|
|
83
|
+
section.questions.each do |question|
|
|
84
|
+
question.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless question.dependency.nil?
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def to_yml
|
|
92
|
+
out =[ %(survey_#{@id}:) ]
|
|
93
|
+
out << %( id: #{@id})
|
|
94
|
+
out << %( title: "#{@title}")
|
|
95
|
+
out << %( description: "#{@description}")
|
|
96
|
+
out << %( access_code: #{@access_code})
|
|
97
|
+
out << %( active_at: #{@active_at})
|
|
98
|
+
out << %( inactive_at: #{@inactive_at})
|
|
99
|
+
(out << nil ).join("\r\n")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def to_file
|
|
103
|
+
"survey -#{self.title}- written to file..."
|
|
104
|
+
File.open(self.parser.surveys_yml, File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
|
|
105
|
+
self.survey_sections.compact.map(&:to_file)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/survey'
|
|
2
|
+
|
|
3
|
+
class SurveyParser
|
|
4
|
+
# Children, Counters, Files
|
|
5
|
+
attr_accessor :surveys
|
|
6
|
+
attr_accessor :last_survey_id, :last_survey_section_id, :last_question_id, :last_answer_id
|
|
7
|
+
attr_accessor :surveys_yml, :survey_sections_yml, :question_groups_yml, :questions_yml, :answers_yml, :dependencies_yml, :dependency_conditions_yml
|
|
8
|
+
|
|
9
|
+
# no more "ARRRGH, EVIL GLOBALS!!!"
|
|
10
|
+
def initialize
|
|
11
|
+
self.surveys = []
|
|
12
|
+
self.define_counter_methods(%w(survey survey_section question_group question answer dependency dependency_condition))
|
|
13
|
+
self.initialize_fixtures(%w(surveys survey_sections question_groups questions answers dependencies dependency_conditions), File.join(RAILS_ROOT, "surveys", "fixtures"))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# new_survey_id, new_survey_section_id, etc.
|
|
17
|
+
def define_counter_methods(names)
|
|
18
|
+
names.each do |name|
|
|
19
|
+
self.instance_variable_set("@last_#{name}_id", 0)
|
|
20
|
+
# self.class.send is a hack - define_method is private
|
|
21
|
+
self.class.send(:define_method, "new_#{name}_id") do
|
|
22
|
+
self.instance_variable_set("@last_#{name}_id", self.instance_variable_get("@last_#{name}_id") + 1)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# surveys_yml, survey_sections_yml, etc.
|
|
28
|
+
def initialize_fixtures(names, path)
|
|
29
|
+
names.each do |name|
|
|
30
|
+
file = self.instance_variable_set("@#{name}_yml", "#{path}/#{name}.yml")
|
|
31
|
+
File.truncate(file, 0) if File.exist?(file)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.parse(file_name)
|
|
36
|
+
puts "--- Parsing '#{file_name}' ---"
|
|
37
|
+
parser = SurveyParser.new
|
|
38
|
+
parser.instance_eval(File.read(file_name), file_name)
|
|
39
|
+
parser.to_files
|
|
40
|
+
puts "--- End of parsing ---"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def survey(title, &block)
|
|
44
|
+
puts "Survey: #{title}"
|
|
45
|
+
if block_given?
|
|
46
|
+
new_survey = Survey.new(self.new_survey_id, self, title)
|
|
47
|
+
new_survey.instance_eval(&block)
|
|
48
|
+
new_survey.reconcile_dependencies
|
|
49
|
+
add_survey(new_survey)
|
|
50
|
+
puts "Survey added"
|
|
51
|
+
else
|
|
52
|
+
puts "ERROR: A survey cannot be empty!"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def add_survey(survey)
|
|
57
|
+
self.surveys << survey
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_files
|
|
61
|
+
self.surveys.compact.map(&:to_file)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|