surveyor 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/README.md +22 -5
  2. data/VERSION +1 -1
  3. data/app/models/dependency.rb +16 -32
  4. data/app/models/question.rb +1 -1
  5. data/app/models/question_group.rb +1 -1
  6. data/app/models/response.rb +1 -4
  7. data/app/models/response_set.rb +4 -3
  8. data/app/models/survey.rb +0 -1
  9. data/app/models/validation.rb +20 -0
  10. data/app/models/validation_condition.rb +19 -17
  11. data/generators/surveyor/surveyor_generator.rb +9 -5
  12. data/generators/surveyor/templates/migrate/add_display_order_to_surveys.rb +9 -0
  13. data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +15 -4
  14. data/lib/surveyor.rb +16 -0
  15. data/script/surveyor/answer.rb +32 -55
  16. data/script/surveyor/base.rb +61 -0
  17. data/script/surveyor/dependency.rb +4 -41
  18. data/script/surveyor/dependency_condition.rb +13 -38
  19. data/script/surveyor/question.rb +16 -50
  20. data/script/surveyor/question_group.rb +7 -19
  21. data/script/surveyor/specs/answer_spec.rb +15 -52
  22. data/script/surveyor/specs/question_spec.rb +37 -85
  23. data/script/surveyor/specs/spec_helper.rb +6 -0
  24. data/script/surveyor/specs/survey_section_spec.rb +23 -0
  25. data/script/surveyor/specs/validation_condition_spec.rb +20 -0
  26. data/script/surveyor/specs/validation_spec.rb +20 -0
  27. data/script/surveyor/survey.rb +10 -85
  28. data/script/surveyor/survey_parser.rb +160 -38
  29. data/script/surveyor/survey_section.rb +6 -130
  30. data/script/surveyor/validation.rb +19 -0
  31. data/script/surveyor/validation_condition.rb +19 -0
  32. data/spec/lib/surveyor_spec.rb +44 -0
  33. data/spec/models/dependency_spec.rb +9 -16
  34. data/spec/models/question_group_spec.rb +3 -3
  35. data/spec/models/question_spec.rb +1 -1
  36. data/spec/models/validation_condition_spec.rb +29 -0
  37. data/spec/models/validation_spec.rb +27 -0
  38. data/spec/spec_helper.rb +0 -2
  39. data/surveyor.gemspec +12 -7
  40. metadata +12 -7
  41. data/lib/tiny_code.rb +0 -58
  42. data/script/surveyor/columnizer.rb +0 -36
  43. data/script/surveyor/specs/question_dependency_spec.rb +0 -46
  44. data/script/surveyor/specs/question_group_spec.rb +0 -9
  45. data/script/surveyor/specs/section_spec.rb +0 -58
data/README.md CHANGED
@@ -7,12 +7,12 @@ Surveyor is a rails (gem) plugin, that brings surveys to your rails app. Before
7
7
  As a plugin:
8
8
 
9
9
  sudo gem install haml
10
- script/plugin install git://github.com/breakpointer/surveyor.git -r 'tag v0.7.0'
10
+ script/plugin install git://github.com/breakpointer/surveyor.git -r 'tag v0.9.0'
11
11
 
12
12
  Or as a gem plugin:
13
13
 
14
14
  # in environment.rb
15
- config.gem "surveyor", :version => '>=0.7.0', :source => 'http://gemcutter.org'
15
+ config.gem "surveyor", :version => '>=0.9.0', :source => 'http://gemcutter.org'
16
16
 
17
17
  sudo gem install gemcutter
18
18
  gem tumble
@@ -105,11 +105,28 @@ Surveyor depends on Rails 2.3 and the SASS style sheet language, part of HAML (h
105
105
 
106
106
  # Changes
107
107
 
108
+ 0.9.0
109
+
110
+ * validations in dsl and surveyor models
111
+ * preserve underscores in reference identifiers
112
+ * dsl specs, refactoring into base class
113
+ * adding display order to surveys
114
+ * moving columnizer and tiny column functionality to surveyor module
115
+ * columnizer (and tiny code) refactoring, columnizer spec extracted from answer spec
116
+ * cleanup of scopes with joins
117
+ * refactoring dependency
118
+
119
+ 0.8.0
120
+
121
+ * question group dependencies
122
+ * expanded examples in kitchen sink survey
123
+ * specs
124
+
108
125
  0.7.1
109
126
 
110
- custom index page
111
- custom classes and renderers
112
- fixing typo in kitchen sink survey
127
+ * custom index page
128
+ * custom classes and renderers
129
+ * fixing typo in kitchen sink survey
113
130
 
114
131
  0.7.0
115
132
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0
1
+ 0.9.0
@@ -4,9 +4,6 @@ class Dependency < ActiveRecord::Base
4
4
  belongs_to :question_group
5
5
  has_many :dependency_conditions
6
6
 
7
- # Scopes
8
- named_scope :depending_on_questions, lambda {|question_ids| {:joins => :dependency_conditions, :conditions => {:dependency_conditions => {:question_id => question_ids}} }}
9
-
10
7
  # Validations
11
8
  validates_presence_of :rule
12
9
  validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/ #TODO properly formed parenthesis etc.
@@ -26,37 +23,24 @@ class Dependency < ActiveRecord::Base
26
23
  write_attribute(:question_id, i)
27
24
  end
28
25
 
29
- # Is the method that determines if this dependency has been met within
30
- # the provided response set
31
- def met?(response_set)
32
- if keyed_pairs = keyed_conditions(response_set)
33
- return(rule_evaluation(keyed_pairs))
34
- else
35
- return(false)
36
- end
37
- end
38
-
39
- # Pairs up the substitution key with the evaluated condition result for substitution into the rule
40
- # Example: If you have two dependency conditions with rule keys "A" and "B" in the rule "A or B"
41
- # calling keyed_condition_pairs will return {:A => true, :B => false}
42
- def keyed_conditions(response_set)
43
- keyed_pairs = {}
44
- # logger.debug dependency_conditions.inspect
45
- self.dependency_conditions.each do |dc|
46
- keyed_pairs.merge!(dc.to_hash(response_set))
47
- end
48
- return(keyed_pairs)
49
- end
50
-
51
- # Does the substiution and evaluation of the dependency rule with the keyed pairs
52
- def rule_evaluation(keyed_pairs)
53
- # subtitute into rule for evaluation
54
- rgx = Regexp.new(self.dependency_conditions.map{|dc| ["a","o"].include?(dc.rule_key) ? "#{dc.rule_key}(?!nd|r)" : dc.rule_key}.join("|")) # Making a regexp to only look for the keys used in the child conditions
26
+ # Has this dependency has been met in the context of response_set?
27
+ # Substitutes the conditions hash into the rule and evaluates it
28
+ def is_met?(response_set)
29
+ ch = conditions_hash(response_set)
30
+ return false if ch.blank?
55
31
  # logger.debug "rule: #{self.rule.inspect}"
56
32
  # logger.debug "rexp: #{rgx.inspect}"
57
- # logger.debug "keyp: #{keyed_pairs.inspect}"
58
- # logger.debug "subd: #{self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}}"
59
- eval(self.rule.gsub(rgx){|m| keyed_pairs[m.to_sym]}) # returns the evaluation of the rule and the conditions
33
+ # logger.debug "keyp: #{ch.inspect}"
34
+ # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
35
+ rgx = Regexp.new(self.dependency_conditions.map{|dc| ["a","o"].include?(dc.rule_key) ? "#{dc.rule_key}(?!nd|r)" : dc.rule_key}.join("|")) # exclude and, or
36
+ eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
37
+ end
38
+
39
+ # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
40
+ def conditions_hash(response_set)
41
+ hash = {}
42
+ self.dependency_conditions.each{|dc| hash.merge!(dc.to_hash(response_set))}
43
+ return hash
60
44
  end
61
45
 
62
46
  end
@@ -33,7 +33,7 @@ class Question < ActiveRecord::Base
33
33
  self.dependency != nil
34
34
  end
35
35
  def triggered?(response_set)
36
- dependent? ? self.dependency.met?(response_set) : true
36
+ dependent? ? self.dependency.is_met?(response_set) : true
37
37
  end
38
38
  def css_class(response_set)
39
39
  [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
@@ -21,7 +21,7 @@ class QuestionGroup < ActiveRecord::Base
21
21
  self.dependency != nil
22
22
  end
23
23
  def triggered?(response_set)
24
- dependent? ? self.dependency.met?(response_set) : true
24
+ dependent? ? self.dependency.is_met?(response_set) : true
25
25
  end
26
26
  def css_class(response_set)
27
27
  [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
@@ -8,10 +8,7 @@ class Response < ActiveRecord::Base
8
8
 
9
9
  # Validations
10
10
  validates_presence_of :response_set_id, :question_id, :answer_id
11
-
12
- # Named scopes
13
- named_scope :in_section, lambda {|section_id| {:include => :question, :conditions => ['questions.survey_section_id =?', section_id.to_i ]}}
14
-
11
+
15
12
  acts_as_response # includes "as" instance method
16
13
 
17
14
  def selected
@@ -100,11 +100,11 @@ class ResponseSet < ActiveRecord::Base
100
100
  end
101
101
 
102
102
  def unanswered_dependencies
103
- dependencies.select{|d| d.met?(self) and self.has_not_answered_question?(d.question)}.map(&:question)
103
+ dependencies.select{|d| d.is_met?(self) and self.has_not_answered_question?(d.question)}.map(&:question)
104
104
  end
105
105
 
106
106
  def all_dependencies
107
- arr = dependencies.partition{|d| d.met?(self) }
107
+ arr = dependencies.partition{|d| d.is_met?(self) }
108
108
  {:show => arr[0].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group_#{d.question_group_id}"}, :hide => arr[1].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group_#{d.question_group_id}"}}
109
109
  end
110
110
 
@@ -112,7 +112,8 @@ class ResponseSet < ActiveRecord::Base
112
112
 
113
113
  def dependencies
114
114
  question_ids = Question.find_all_by_survey_section_id(current_section_id).map(&:id)
115
- Dependency.depending_on_questions(question_ids)
115
+ depdendecy_ids = DependencyCondition.all(:conditions => {:question_id => question_ids}).map(&:dependency_id)
116
+ Dependency.find(depdendecy_ids, :include => :dependency_conditions)
116
117
  end
117
118
  end
118
119
 
data/app/models/survey.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  class Survey < ActiveRecord::Base
2
- include TinyCode
3
2
 
4
3
  # Associations
5
4
  has_many :sections, :class_name => "SurveySection", :order => 'display_order'
@@ -1,6 +1,7 @@
1
1
  class Validation < ActiveRecord::Base
2
2
  # Associations
3
3
  belongs_to :answer
4
+ has_many :validation_conditions
4
5
 
5
6
  # Scopes
6
7
 
@@ -10,4 +11,23 @@ class Validation < ActiveRecord::Base
10
11
  validates_numericality_of :answer_id
11
12
 
12
13
  # Instance Methods
14
+ def is_valid?(response_set)
15
+ ch = conditions_hash(response_set)
16
+ rgx = Regexp.new(self.validation_conditions.map{|vc| ["a","o"].include?(vc.rule_key) ? "#{vc.rule_key}(?!nd|r)" : vc.rule_key}.join("|")) # exclude and, or
17
+ # logger.debug "v: #{self.inspect}"
18
+ # logger.debug "rule: #{self.rule.inspect}"
19
+ # logger.debug "rexp: #{rgx.inspect}"
20
+ # logger.debug "keyp: #{ch.inspect}"
21
+ # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
22
+ eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
23
+ end
24
+
25
+ # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
26
+ def conditions_hash(response_set)
27
+ hash = {}
28
+ response = response_set.responses.detect{|r| r.answer_id.to_i == self.answer_id.to_i}
29
+ # logger.debug "r: #{response.inspect}"
30
+ self.validation_conditions.each{|vc| hash.merge!(vc.to_hash(response))}
31
+ return hash
32
+ end
13
33
  end
@@ -13,27 +13,29 @@ class ValidationCondition < ActiveRecord::Base
13
13
  validates_inclusion_of :operator, :in => OPERATORS
14
14
  validates_uniqueness_of :rule_key, :scope => :validation_id
15
15
 
16
+ acts_as_response # includes "as" instance method
17
+
16
18
  # Class methods
17
19
  def self.operators
18
20
  OPERATORS
19
21
  end
20
22
 
21
23
  # Instance Methods
22
- # def to_hash(answer, response)
23
- # {rule_key.to_sym => self.is_valid?(response)}
24
- # end
25
- #
26
- # def is_valid?(response)
27
- # klass = response.answer.response_class
28
- # case self.operator
29
- # when "==", "<", ">", "<=", ">="
30
- # response.as(klass).send(self.operator, self.as(klass))
31
- # when "!="
32
- # !(response.as(klass) == self.as(klass))
33
- # when "=~"
34
- # response.as(klass).to_s =~ self.regexp
35
- # else
36
- # false
37
- # end
38
- # end
24
+ def to_hash(response)
25
+ {rule_key.to_sym => (response.nil? ? false : self.is_valid?(response))}
26
+ end
27
+
28
+ def is_valid?(response)
29
+ klass = response.answer.response_class
30
+ case self.operator
31
+ when "==", "<", ">", "<=", ">="
32
+ response.as(klass).send(self.operator, self.as(klass))
33
+ when "!="
34
+ !(response.as(klass) == self.as(klass))
35
+ when "=~"
36
+ !(response.as(klass).to_s =~ Regexp.new(self.regexp || "")).nil?
37
+ else
38
+ false
39
+ end
40
+ end
39
41
  end
@@ -25,12 +25,16 @@ class SurveyorGenerator < Rails::Generator::Base
25
25
  # not using m.migration_template because all migration timestamps end up the same, causing a collision when running rake db:migrate
26
26
  # coped functionality from RAILS_GEM_PATH/lib/rails_generator/commands.rb
27
27
  m.directory "db/migrate"
28
- ["surveys", "survey_sections", "questions", "question_groups", "answers", "response_sets", "responses", "dependencies", "dependency_conditions", "validations", "validation_conditions"].each_with_index do |model, i|
29
- unless (prev_migrations = Dir.glob("db/migrate/[0-9]*_*.rb").grep(/[0-9]+_create_#{model}.rb$/)).empty?
30
- prev_migration_timestamp = prev_migrations[0].match(/([0-9]+)_create_#{model}.rb$/)[1]
28
+ [ "create_surveys", "create_survey_sections", "create_questions", "create_question_groups", "create_answers",
29
+ "create_response_sets", "create_responses",
30
+ "create_dependencies", "create_dependency_conditions",
31
+ "create_validations", "create_validation_conditions",
32
+ "add_display_order_to_surveys"].each_with_index do |model, i|
33
+ unless (prev_migrations = Dir.glob("db/migrate/[0-9]*_*.rb").grep(/[0-9]+_#{model}.rb$/)).empty?
34
+ prev_migration_timestamp = prev_migrations[0].match(/([0-9]+)_#{model}.rb$/)[1]
31
35
  end
32
- # raise "Another migration is already named create_#{model}" if not Dir.glob("db/migrate/[0-9]*_*.rb").grep(/[0-9]+_create_#{model}.rb$/).empty?
33
- m.template("migrate/create_#{model}.rb", "db/migrate/#{(prev_migration_timestamp || Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + i).to_s}_create_#{model}.rb")
36
+ # raise "Another migration is already named #{model}" if not Dir.glob("db/migrate/[0-9]*_*.rb").grep(/[0-9]+_#{model}.rb$/).empty?
37
+ m.template("migrate/#{model}.rb", "db/migrate/#{(prev_migration_timestamp || Time.now.utc.strftime("%Y%m%d%H%M%S").to_i + i).to_s}_#{model}.rb")
34
38
  end
35
39
 
36
40
  # Generate CSS
@@ -0,0 +1,9 @@
1
+ class AddDisplayOrderToSurveys < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :surveys, :display_order, :integer
4
+ end
5
+
6
+ def self.down
7
+ remove_column :surveys, :display_order
8
+ end
9
+ end
@@ -23,7 +23,7 @@ survey "&#8220;Kitchen Sink&#8221; survey" do
23
23
 
24
24
  # A dependent question, with conditions and rule to logically join them
25
25
  # the question's reference identifier is "2a", and the answer's reference_identifier is "1"
26
- # question reference identifiers used in conditions need to be unique for the lookups to work
26
+ # question reference identifiers used in conditions need to be unique on a survey for the lookups to work
27
27
  q_2a "Please explain why you don't like this color?"
28
28
  a_1 "explanation", :text
29
29
  dependency :rule => "A or B or C or D"
@@ -62,22 +62,33 @@ survey "&#8220;Kitchen Sink&#8221; survey" do
62
62
  a "what", :string
63
63
  a "where", :string
64
64
 
65
- # Various types of respones can be accepted
65
+ # Various types of responses can be accepted, and validated
66
66
  q "How many pets do you own?"
67
67
  a :integer
68
+ validation :rule => "A"
69
+ condition_A ">=", :integer_value => 0
68
70
 
69
71
  # Surveys, sections, questions, groups, and answers also take a custom css class for covenience in custom styling
70
72
  q "What is your address?", :custom_class => 'address'
71
73
  a :text, :custom_class => 'mapper'
74
+ # validations can use regexp values
75
+ validation :rule => "A"
76
+ condition_A "=~", :regexp => /[0-9a-zA-z\. #]/
72
77
 
73
78
  # Questions, groups, and answers take a custom renderer (a partial in the application's views dir)
74
79
  # defaults are "/partials/question_group", "/partials/question", "/partials/answer", so the custom renderers should have a different name
75
80
  q "Pick your favorite date AND time" #, :custom_renderer => "/partials/custom_question"
76
81
  a :datetime
77
82
 
78
- q "What time do you usually take a lunch break?"
79
- a :time
83
+ q_time_lunch "What time do you usually take a lunch break?"
84
+ a_1 :time
80
85
 
86
+ # # validation conditions can look up the response to another question/answer pair
87
+ # q_time_dinner "What time do you usually take a dinner break?"
88
+ # a :time
89
+ # validation :rule => "A"
90
+ # condition_A ">=", :question_reference => "time_lunch", :answer_reference => "1"
91
+
81
92
  q "When would you like to meet for dinner?"
82
93
  a :date
83
94
 
data/lib/surveyor.rb CHANGED
@@ -5,4 +5,20 @@ require 'surveyor/config'
5
5
  require 'surveyor/acts_as_response'
6
6
 
7
7
  module Surveyor
8
+ RAND_CHARS = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten.to_s
9
+
10
+ def self.make_tiny_code(len = 10)
11
+ len.times.map{|i| RAND_CHARS[rand(RAND_CHARS.size), 1] }.to_s
12
+ end
13
+
14
+ def self.to_normalized_string(text)
15
+ words_to_omit = %w(a be but has have in is it of on or the to when)
16
+ col_text = text.gsub(/(<[^>]*>)|\n|\t/s, ' ') # Remove html tags
17
+ col_text.downcase! # Remove capitalization
18
+ col_text.gsub!(/\"|\'/, '') # Remove potential problem characters
19
+ col_text.gsub!(/\(.*?\)/,'') # Remove text inside parens
20
+ col_text.gsub!(/\W/, ' ') # Remove all other non-word characters
21
+ cols = (col_text.split(' ') - words_to_omit)
22
+ (cols.size > 5 ? cols[-5..-1] : cols).join("_")
23
+ end
8
24
  end
@@ -1,75 +1,52 @@
1
- require File.dirname(__FILE__) + '/columnizer'
2
-
3
- class Answer
4
- include Columnizer
5
-
1
+ class Answer < SurveyParser::Base
6
2
  # Context, Content, Reference, Display
7
3
  attr_accessor :id, :parser, :question_id
8
4
  attr_accessor :text, :short_text, :help_text, :weight, :response_class
9
5
  attr_accessor :reference_identifier, :data_export_identifier, :common_namespace, :common_identitier
10
6
  attr_accessor :display_order, :is_exclusive, :hide_label, :display_length, :custom_class, :custom_renderer
11
-
12
- def initialize(question, args, options)
13
- self.parser = question ? question.parser : nil
14
- self.id = parser ? parser.new_answer_id : nil
15
- self.question_id = question ? question.id : nil
7
+ attr_accessor :validation
16
8
 
17
- #self.text is set in args
18
- args_options = parse_args_options(args)
19
- self.default_options(args_options[:text]).merge(options).merge(args_options).each{|key,value| self.instance_variable_set("@#{key}", value)}
20
- end
21
-
22
- def default_options(text)
23
- { :short_text => text,
24
- :data_export_identifier => Columnizer.to_normalized_column(text),
25
- :is_exclusive => false,
9
+ def default_options
10
+ { :is_exclusive => false,
26
11
  :hide_label => false,
27
12
  :response_class => "answer"
28
13
  }
29
14
  end
30
15
 
31
- def parse_args_options(args)
32
- a0, a1, a2 = args
33
-
34
- # Hash
35
- if a0.is_a?(Hash)
36
- {:text => "Answer"}.merge(a0)
37
-
38
- # (String, Hash) or (String, Symbol, Hash)
39
- elsif a0.is_a?(String)
40
- a1.is_a?(Symbol) ? {:text => a0, :response_class => a1.to_s}.merge(a2 || {}) : {:text => a0}.merge(a1 || {})
41
-
42
- # (Symbol, Hash) or (Symbol, Symbol, Hash)
43
- elsif a0.is_a?(Symbol)
44
- shortcuts = case a0
45
- when :other
46
- {:text => "Other"}
47
- when :other_and_string
48
- {:text => "Other", :response_class => "string"}
49
- when :none, :omit #a disabler erases and disables all other answer options (text, checkbox, dropdowns, etc). Unchecking the omit box is the only way to enable other options (except in the case limit => :one)
50
- {:text => a0.to_s.humanize, :is_exclusive => true} # "omit" is no longer a response class... it's treated as any other answer type
51
- when :integer, :date, :time, :datetime, :text, :datetime, :string
52
- {:text => a0.to_s.humanize, :response_class => a0.to_s, :hide_label => true}
53
- else
54
- {:text => a0.to_s.humanize}
55
- end
56
- a1.is_a?(Symbol) ? shortcuts.merge({:response_class => a1.to_s}).merge(a2 || {}) : shortcuts.merge(a1 || {})
16
+ def parse_args(args)
17
+ case args[0]
18
+ when Hash # Hash
19
+ text_args(args[0][:text]).merge(args[0])
20
+ when String # (String, Hash) or (String, Symbol, Hash)
21
+ text_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
22
+ when Symbol # (Symbol, Hash) or (Symbol, Symbol, Hash)
23
+ symbol_args(args[0]).merge(hash_from args[1]).merge(args[2] || {})
57
24
  else
58
- {:text => "Answer"}
25
+ text_args(nil)
59
26
  end
60
27
  end
61
-
62
- def yml_attrs
63
- instance_variables.sort - ["@parser"]
28
+
29
+ def text_args(text = "Answer")
30
+ {:text => text.to_s, :short_text => text, :data_export_identifier => Surveyor.to_normalized_string(text)}
64
31
  end
65
- def to_yml
66
- out = [ %(#{@data_export_identifier}_#{@id}:) ]
67
- yml_attrs.each{|a| out << " #{a[1..-1]}: #{instance_variable_get(a).is_a?(String) ? "\"#{instance_variable_get(a)}\"" : instance_variable_get(a) }"}
68
- (out << nil ).join("\r\n")
32
+ def hash_from(arg)
33
+ arg.is_a?(Symbol) ? {:response_class => arg.to_s} : arg.is_a?(Hash) ? arg : {}
34
+ end
35
+ def symbol_args(arg)
36
+ case arg
37
+ when :other
38
+ text_args("Other")
39
+ when :other_and_string
40
+ text_args("Other").merge({:response_class => "string"})
41
+ when :none, :omit # is_exclusive erases and disables other checkboxes and input elements
42
+ text_args(arg.to_s.humanize).merge({:is_exclusive => true})
43
+ when :integer, :date, :time, :datetime, :text, :datetime, :string
44
+ text_args(arg.to_s.humanize).merge({:response_class => arg.to_s, :hide_label => true})
45
+ end
69
46
  end
70
-
71
47
  def to_file
72
- File.open(self.parser.answers_yml, File::CREAT|File::APPEND|File::WRONLY){ |f| f << to_yml }
48
+ super
49
+ if self.validation then self.validation.to_file end
73
50
  end
74
51
 
75
52
  end