surveyor 0.8.0 → 0.9.0

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.
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