question_chain 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +18 -0
  3. data/Licence.txt +20 -0
  4. data/README.rdoc +34 -0
  5. data/Rakefile +2 -0
  6. data/config/initializers/mustache.rb +2 -0
  7. data/lib/question_chain/answerable.rb +142 -0
  8. data/lib/question_chain/answers.rb +290 -0
  9. data/lib/question_chain/models/answers/question_view.rb +21 -0
  10. data/lib/question_chain/models/answers/ui_object_view.rb +67 -0
  11. data/lib/question_chain/models/answers/ui_objects_check_box_view.rb +17 -0
  12. data/lib/question_chain/models/answers/ui_objects_drop_down_view.rb +16 -0
  13. data/lib/question_chain/models/answers/ui_objects_hidden_field_view.rb +8 -0
  14. data/lib/question_chain/models/answers/ui_objects_object_reference_drop_down_view.rb +18 -0
  15. data/lib/question_chain/models/answers/ui_objects_object_search_view.rb +22 -0
  16. data/lib/question_chain/models/answers/ui_objects_relatable_category_drop_down_view.rb +18 -0
  17. data/lib/question_chain/models/answers/ui_objects_text_field_view.rb +8 -0
  18. data/lib/question_chain/models/chain_template.rb +43 -0
  19. data/lib/question_chain/models/question.rb +37 -0
  20. data/lib/question_chain/models/relatable_category_filter.rb +15 -0
  21. data/lib/question_chain/models/rule.rb +27 -0
  22. data/lib/question_chain/models/rules/attribute_change.rb +28 -0
  23. data/lib/question_chain/models/rules/choice_genenerator.rb +5 -0
  24. data/lib/question_chain/models/rules/populate_drop_down.rb +30 -0
  25. data/lib/question_chain/models/rules/search.rb +19 -0
  26. data/lib/question_chain/models/rules/value_change.rb +22 -0
  27. data/lib/question_chain/models/ui_group.rb +71 -0
  28. data/lib/question_chain/models/ui_object.rb +92 -0
  29. data/lib/question_chain/models/ui_object_answer.rb +17 -0
  30. data/lib/question_chain/models/ui_objects/check_box.rb +8 -0
  31. data/lib/question_chain/models/ui_objects/drop_down.rb +17 -0
  32. data/lib/question_chain/models/ui_objects/hidden_field.rb +5 -0
  33. data/lib/question_chain/models/ui_objects/object_reference_drop_down.rb +67 -0
  34. data/lib/question_chain/models/ui_objects/object_search.rb +44 -0
  35. data/lib/question_chain/models/ui_objects/radio_button.rb +5 -0
  36. data/lib/question_chain/models/ui_objects/radio_button_group.rb +5 -0
  37. data/lib/question_chain/models/ui_objects/relatable_category_drop_down.rb +70 -0
  38. data/lib/question_chain/models/ui_objects/text_field.rb +11 -0
  39. data/lib/question_chain/mongo_serialization.rb +62 -0
  40. data/lib/question_chain/mustache_handler.rb +16 -0
  41. data/lib/question_chain/mustache_rails.rb +50 -0
  42. data/lib/question_chain/state_machine.rb +29 -0
  43. data/lib/question_chain/stored_template.rb +30 -0
  44. data/lib/question_chain/version.rb +3 -0
  45. data/lib/question_chain/views/answers/_edit.html.haml +62 -0
  46. data/lib/question_chain/views/answers/_new.html.haml +62 -0
  47. data/lib/question_chain/views/answers/_question.html.mustache +11 -0
  48. data/lib/question_chain/views/answers/_ui_objects_check_box.html.mustache +19 -0
  49. data/lib/question_chain/views/answers/_ui_objects_drop_down.html.mustache +26 -0
  50. data/lib/question_chain/views/answers/_ui_objects_hidden_field.html.mustache +3 -0
  51. data/lib/question_chain/views/answers/_ui_objects_object_reference_drop_down.html.mustache +27 -0
  52. data/lib/question_chain/views/answers/_ui_objects_object_search.html.mustache +20 -0
  53. data/lib/question_chain/views/answers/_ui_objects_relatable_category_drop_down.html.mustache +26 -0
  54. data/lib/question_chain/views/answers/_ui_objects_text_field.html.mustache +19 -0
  55. data/lib/question_chain/views/layouts/application.html.haml +10 -0
  56. data/lib/question_chain.rb +35 -0
  57. data/question_chain.gemspec +31 -0
  58. data/test_app/.gitignore +4 -0
  59. data/test_app/Gemfile +29 -0
  60. data/test_app/Rakefile +16 -0
  61. data/test_app/app/controllers/answers_controller.rb +13 -0
  62. data/test_app/app/controllers/application_controller.rb +4 -0
  63. data/test_app/app/models/container.rb +10 -0
  64. data/test_app/app/models/flight.rb +20 -0
  65. data/test_app/app/views/answers/edit.html.haml +1 -0
  66. data/test_app/app/views/answers/new.html.haml +1 -0
  67. data/test_app/config/application.rb +18 -0
  68. data/test_app/config/boot.rb +13 -0
  69. data/test_app/config/database.yml +15 -0
  70. data/test_app/config/environment.rb +5 -0
  71. data/test_app/config/initializers/app.rb +5 -0
  72. data/test_app/config/initializers/cookie_verification_secret.rb +7 -0
  73. data/test_app/config/initializers/mongodb.rb +2 -0
  74. data/test_app/config/initializers/session_store.rb +3 -0
  75. data/test_app/config/routes.rb +30 -0
  76. data/test_app/config.ru +2 -0
  77. data/test_app/environments/development.rb +19 -0
  78. data/test_app/environments/test.rb +30 -0
  79. data/test_app/lib/tasks/rspec.rake +69 -0
  80. data/test_app/lib/tasks/yard.rake +4 -0
  81. data/test_app/public/.gitkeep +0 -0
  82. data/test_app/script/rails +10 -0
  83. data/test_app/spec/acceptance/new_spec +30 -0
  84. data/test_app/spec/factories.rb +81 -0
  85. data/test_app/spec/models/chain_template_spec.rb +53 -0
  86. data/test_app/spec/models/flight_spec.rb +101 -0
  87. data/test_app/spec/models/question_spec.rb +13 -0
  88. data/test_app/spec/models/rules/value_change_spec.rb +31 -0
  89. data/test_app/spec/models/ui_group_spec.rb +55 -0
  90. data/test_app/spec/models/ui_object_spec.rb +33 -0
  91. data/test_app/spec/models/ui_objects/drop_down_spec.rb +28 -0
  92. data/test_app/spec/models/ui_objects/relatable_category_drop_down_spec.rb +13 -0
  93. data/test_app/spec/spec_helper.rb +25 -0
  94. metadata +325 -0
@@ -0,0 +1,18 @@
1
+ module Answers
2
+ class UiObjectsRelatableCategoryDropDownView < Answers::UiObjectView
3
+
4
+ def prompt
5
+ ui_object.prompt
6
+ end
7
+
8
+ # @return [Array<Hash<value => value, name => name>>]
9
+ def options
10
+ if answer_params
11
+ ui_object.options.map{|option| option.merge!(:selected => option["value"].to_s == value.to_s)}
12
+ else
13
+ ui_object.options
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ module Answers
2
+ class UiObjectsTextFieldView < Answers::UiObjectView
3
+
4
+ def value
5
+ super || default_value
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ class ChainTemplate
2
+ include MongoMapper::Document
3
+ include MongoMapper::StateMachine
4
+
5
+ # == Keys
6
+ key :context, Hash # [Hash<content => [Array<MongoIds>]>]
7
+ key :account_id, ObjectId # used to override any specific question essentially
8
+ key :for_resource, String
9
+ key :parent_resource, String
10
+ key :active_at, Time
11
+
12
+ # == State Machine
13
+ state_machine :initial => :pending do
14
+ state :pending
15
+ state :active
16
+
17
+ event :activate do
18
+ transitions :to => :active, :from => [:pending], :on_transition => :set_active_at
19
+ end
20
+
21
+ event :deactivate do
22
+ transitions :to => :pending, :from => [:active]
23
+ end
24
+ end
25
+
26
+ # == Indexes
27
+ ensure_index :for_resource
28
+
29
+ # == Validations
30
+ validates_presence_of :for_resource
31
+ validates_presence_of :context
32
+ validates_true_for :context, :logic => Proc.new{ !context.empty?}
33
+
34
+ def self.attributes_for_api
35
+ %w(id name for_resource account_id parent_resource)
36
+ end
37
+
38
+ protected
39
+ def set_active_at
40
+ self.active_at = Time.now
41
+ self.save!
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ class Question
2
+ include MongoMapper::Document
3
+ include MongoMapper::Serialize
4
+
5
+ # == Keys
6
+ key :name, String
7
+ key :label, String
8
+ key :_type, String
9
+ key :description, String
10
+ key :calculator_id, ObjectId
11
+ key :computation_id, ObjectId
12
+ timestamps!
13
+
14
+ # == Indexes
15
+ ensure_index :names
16
+ ensure_index :label
17
+
18
+ # == Validations
19
+ validates_presence_of :name
20
+ validates_uniqueness_of :name
21
+
22
+ # == Associations
23
+ many :ui_groups, :order => :position.asc, :dependent => :destroy
24
+
25
+ # == Hooks
26
+ def self.attributes_for_api
27
+ %w(id name description computation_id ui_groups label calculator_id)
28
+ end
29
+
30
+ def to_json
31
+ attributes_for_api_resource.to_json
32
+ end
33
+
34
+ def to_hash
35
+ Hashie::Mash.new(attributes_for_api_resources)
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ class RelatableCategoryFilter
2
+ include MongoMapper::Document
3
+ include MongoMapper::Serialize
4
+
5
+ # == Keys
6
+ key :filters, Array
7
+ key :ui_group_id, ObjectId
8
+
9
+ # == Associations
10
+ belongs_to :ui_group
11
+
12
+ def self.attributes_for_api
13
+ %w(id filters ui_group_id)
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ class Rule
2
+ include MongoMapper::EmbeddedDocument
3
+ include MongoMapper::Serialize
4
+
5
+ # == Keys
6
+ key :fire_value, String
7
+ key :negate_value, :default => false
8
+ key :_type, String
9
+
10
+ def ui_object_id
11
+ _parent_document.id
12
+ end
13
+
14
+ # have to have this here to declear all subclass not being used?
15
+ def self.attributes_for_api
16
+ %w(id fire_value _type ui_object_id negate_value)
17
+ end
18
+
19
+ # this will manipulate the question hash with the updated
20
+ # attributes for the affecting ui objects that are
21
+ # attached to this rule
22
+ #
23
+ # @return [TrueClass] when the fire! is actually doing that
24
+ def fire!(value, question_hash = {})
25
+ raise NotImplementedError, "Need to set the fire! method"
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module Rules
2
+ class AttributeChange < Rule
3
+ # {:id => {:atttibute_name => "new_state"}, :id => {:atttibute_name => "new_state"}}
4
+ key :affecting_ui_objects, Hash
5
+ key :compare_text_value, :default => false
6
+
7
+ def self.attributes_for_api
8
+ %w(id fire_value _type affecting_ui_objects ui_object_id negate_value compare_text_value)
9
+ end
10
+
11
+ # Checks to determine the value and the fire_value if cool
12
+ # then the ui objects are updated depending on the affecting values
13
+ #
14
+ # @param [UiObject] ui_object the ui object that this rul is observing
15
+ # @param [String] value the value that the ui_object has
16
+ # @param [Array<Hash>] ui_objects
17
+ def fire!(value, ui_objects)
18
+ if negate_value ? value != self.fire_value : value == self.fire_value
19
+ affecting_ui_objects.each_pair do |key, value|
20
+ if ui_object = ui_objects.detect{|ui| ui["id"] == key}
21
+ ui_object["ui_attributes"].merge!(value)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Rules
2
+ class ChoiceGenerator < Rule
3
+
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ module Rules
2
+ class PopulateDropDown < Rule
3
+
4
+ # == Keys
5
+ key :ui_object_attribute_check, Hash, :default => nil
6
+
7
+ def fire!(value = nil, ui_objects_hash = {})
8
+ # does not matter what the value is in this instance
9
+ # it is used to get opions for the drop_down_target
10
+ end
11
+
12
+ def drop_down_target_id
13
+ _parent_document.drop_down_target_id
14
+ end
15
+
16
+ # ask the parent document to get the options
17
+ def get_options(object_ids = [])
18
+ options = []
19
+ _parent_document.get_target_drop_down_options(object_ids).each_pair do |key ,value|
20
+ options << {:name => value, :value => key}
21
+ end
22
+ options.sort_by{|option| option[:name]}
23
+ end
24
+
25
+ def self.attributes_for_api
26
+ %w(id fire_value _type ui_object_id drop_down_target_id ui_object_attribute_check negate_value)
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ module Rules
2
+ class Search < Rule
3
+
4
+ def fire!(value = nil, ui_objects_hash = {})
5
+ # does not matter what the value is in this instance
6
+ # it is used to get opions for the drop_down_target
7
+ end
8
+
9
+ # ask the parent document to get the options
10
+ def get_options(filter, relatable_category_names = [])
11
+ _parent_document.options(filter, relatable_category_names)
12
+ end
13
+
14
+ def self.attributes_for_api
15
+ %w(id fire_value _type ui_object_id negate_value)
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Rules
2
+ class ValueChange < Rule
3
+
4
+ # == Keys
5
+ key :affecting_ui_object_id, ObjectId
6
+ key :change_value, String
7
+
8
+ # == Validations
9
+ validates_presence_of :affecting_ui_object_id
10
+ validates_presence_of :change_value
11
+
12
+ def fire!(value = nil, ui_objects_hash = {})
13
+ # does not matter what the value is in this instance
14
+ # it is used to get opions for the drop_down_target
15
+ end
16
+
17
+ def self.attributes_for_api
18
+ %w(id fire_value _type ui_object_id affecting_ui_object_id negate_value)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,71 @@
1
+ # holds a group of ui objects basically
2
+ # keep simple to start of with
3
+ class UiGroup
4
+ include MongoMapper::Document
5
+ include MongoMapper::Serialize
6
+
7
+ # == Keys
8
+ key :name, String
9
+ key :description, String
10
+ key :label, String
11
+ key :parent_id, ObjectId
12
+ key :parent_ids, Array
13
+ key :question_id, ObjectId
14
+ key :ui_attributes, Hash
15
+ key :css_classes, Array, :default => ["single"]
16
+ timestamps!
17
+
18
+ # == Indexes
19
+ ensure_index :parent_ids
20
+ ensure_index :name
21
+
22
+ # == Validations
23
+ validates_presence_of :name
24
+ validates_presence_of :label
25
+ validates_presence_of :question_id
26
+
27
+ # == Associations
28
+ belongs_to :parent
29
+ belongs_to :question
30
+ many :ui_objects, :order => :position.asc
31
+ many :object_searches, :class_name => "UiObjects::ObjectSearch"
32
+ many :text_fields, :class_name => "UiObjects::TextField"
33
+ many :drop_downs, :class_name => "UiObjects::DropDown"
34
+ many :checkboxes, :class_name => "UiObjects::CheckBox"
35
+ one :object_reference_drop_down, :class_name => "UiObjects::ObjectReferenceDropDown"
36
+ many :relatable_category_drop_downs, :class_name => "UiObjects::RelatableCategoryDropDown"
37
+ many :hidden_fields, :class_name => "UiObjects::HiddenField"
38
+ many :children, :class_name => 'UiGroup', :foreign_key => 'parent_id'
39
+ one :relatable_category_filter, :class_name => "RelatableCategoryFilter"
40
+
41
+ # thats right only ever one object_reference in a ui_group
42
+ # its a constraint we just need
43
+ one :object_reference_drop_down, :class_name => "UiObjects::ObjectReferenceDropDown"
44
+
45
+ # == hooks
46
+ before_save :set_parents
47
+
48
+ def self.attributes_for_api
49
+ %w(id name label question_id relatable_category_filter ui_objects ui_attributes default_styles css_classes)
50
+ end
51
+
52
+ # this should go into the mustache view basically
53
+ def default_styles
54
+ default_styles = ""
55
+ self.ui_attributes.each_pair do |key, value|
56
+ if key.to_s == "visible" && value == false
57
+ default_styles << "display:none;visibility:hidden;"
58
+ end
59
+ end
60
+ default_styles
61
+ end
62
+
63
+ def css_classes
64
+ read_attribute(:css_classes).join(" ")
65
+ end
66
+
67
+ protected
68
+ def set_parents
69
+ self.parent_ids = (parent.parent_ids || []) << parent_id if parent?
70
+ end
71
+ end
@@ -0,0 +1,92 @@
1
+ require "observer"
2
+ class UiObject
3
+ include ::Observable
4
+ include MongoMapper::Document
5
+ include MongoMapper::Serialize
6
+
7
+ # == Keys
8
+ key :_type, String
9
+ key :name, String
10
+ key :label, String
11
+ key :description, String
12
+ key :default_value, String
13
+ key :ui_attributes, Hash
14
+ key :ui_group_id, ObjectId
15
+ key :position, Integer
16
+ key :extra_info, String
17
+ key :per_page, Integer, :default => 50
18
+ key :css_classes, Array, :default => ["single"]
19
+ timestamps!
20
+
21
+ # == Indexes
22
+ ensure_index :label
23
+ ensure_index "rules.name"
24
+
25
+ # == Attrs
26
+ attr_accessor :value
27
+ class_inheritable_accessor :default_values
28
+
29
+ # Class set values
30
+ self.default_values = {:visible => true, :enabled => true}
31
+
32
+ # == Validations
33
+ validates_presence_of :ui_group_id
34
+ validates_presence_of :label
35
+ validates_presence_of :ui_attributes
36
+ validates_true_for :ui_attributes, :logic => Proc.new{
37
+ !ui_attributes.empty?
38
+ }
39
+ validates_associated :rules
40
+
41
+ # == Associations
42
+ belongs_to :ui_group
43
+ many :rules
44
+
45
+ # == Hooks
46
+ before_save :add_rule_observers!
47
+ before_validation_on_create :set_default_attributes
48
+
49
+ def self.attributes_for_api
50
+ %w(id name _type label description default_value ui_attributes ui_group_id rules extra_info css_classes)
51
+ end
52
+
53
+ # == need as added a little late in the day basically
54
+ def per_page
55
+ read_attribute(:per_page) || 50
56
+ end
57
+
58
+ def initialize(*args)
59
+ super
60
+ add_rule_observers!
61
+ end
62
+
63
+ # fires the rules that are attached the ui object
64
+ def change_value!(ui_objects = [])
65
+ if ui_object = ui_objects.detect{|ui| ui["id"] == self.id.to_s}
66
+ value = ui_object["ui_attributes"] && ui_object["ui_attributes"]["value"] || ""
67
+ @observer_state = true
68
+ notify_observers(value, ui_objects)
69
+ end
70
+ end
71
+
72
+ def visible?
73
+ ui_attributes[:visible]
74
+ end
75
+
76
+ def enabled?
77
+ ui_attributes[:enabled]
78
+ end
79
+
80
+ protected
81
+ def set_default_attributes
82
+ self.ui_attributes = self.class.default_values.merge(:value => self.default_value || "").merge(self.ui_attributes || {})
83
+ end
84
+
85
+ # Adding the obervers so we can fire rules
86
+ def add_rule_observers!
87
+ rules.each do |rule|
88
+ self.add_observer(rule, :fire!)
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,17 @@
1
+ class UiObjectAnswer
2
+
3
+ # @todo refactor make not as ugly as a bulldog eating a wasp
4
+ # @param [String] json
5
+ # @return [String] json formatted String manipulate from ui changes
6
+ def self.update_answers!(answer_json = "")
7
+ @answer_hash = JSON.parse(answer_json)
8
+ if @ui_objects = @answer_hash.delete("ui_objects")
9
+ uis = UiObject.find(ui_object_ids)
10
+ uis.each do |ui_object|
11
+ ui_object.change_value!(@ui_objects)
12
+ end
13
+ end
14
+ @answer_hash["ui_objects"] = @ui_objects
15
+ answer_json = @answer_hash.to_json
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module UiObjects
2
+ class CheckBox < UiObject
3
+
4
+ # == Added defaults
5
+ self.default_values = self.default_values.merge!(:checked => false)
6
+
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module UiObjects
2
+ class DropDown < UiObject
3
+
4
+ # == keys
5
+ key :options, Array
6
+ key :prompt, String
7
+ key :max_options, Integer, :default => 20
8
+ key :populate, Boolean, :default => true
9
+ key :order, String
10
+ key :filter, Set
11
+
12
+ def self.attributes_for_api
13
+ %w(id name _type populate label order filter description default_value ui_attributes options prompt max_options rules extra_info css_classes)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module UiObjects
2
+ class HiddenField < UiObject
3
+ key :name, String, :default => "computation_id"
4
+ end
5
+ end
@@ -0,0 +1,67 @@
1
+ module UiObjects
2
+ class ObjectReferenceDropDown < UiObject
3
+
4
+ # == Keys
5
+ key :prompt, String
6
+ key :max_options, Integer, :default => 20
7
+
8
+ # :id => :attribute_name # not quite sure about the option trackers
9
+ key :object_name, String
10
+ key :filters, Set # filters
11
+ key :filter_attribute, String
12
+ key :filter_value, String
13
+ key :order, String
14
+ key :drop_down_target_id, ObjectId
15
+ key :drop_down_target_options_filters, Array # == Array of filters for formula inputs
16
+ key :populate, Boolean, :default => true
17
+ key :relatable_options, Boolean, :default => false
18
+ key :attribute_for_value, String, :default => "id"
19
+ key :attribute_for_display, String, :default => "identifier"
20
+ key :external, Boolean, :default => true
21
+
22
+ # == Indexes
23
+ ensure_index :object_name
24
+
25
+ # == Validations
26
+ validates_presence_of :object_name
27
+
28
+ def options
29
+ return [] if !populate
30
+ @options ||= get_options(object_name, filters || [])
31
+ end
32
+
33
+ def self.attributes_for_api
34
+ %w(id name _type label description rules default_value ui_attributes options prompt max_options object_name drop_down_target_id extra_info css_classes)
35
+ end
36
+
37
+ # object_reference drop down can only target its formula units basically
38
+ # only 1 object in this instance
39
+ def get_target_drop_down_options(object_ids = [])
40
+ if object_id = object_ids.first
41
+ formula_inputs = QuestionChain.calculated_session.formula_inputs_for_generic_object(object_id.to_s)
42
+ if respond_to?(:drop_down_target_options_filters) && !drop_down_target_options_filters.empty?
43
+ selected_formula_inputs = formula_inputs.select{|f| drop_down_target_options_filters.map(&:downcase).include?(f.name.downcase)}
44
+ selected_formula_inputs.inject({}){|var, fi| var.merge!({fi.name => fi.label_input_units}); var}
45
+ else
46
+ formula_inputs.inject({}){|var, fi| var.merge!({fi.name => fi.label_input_units}); var}
47
+ end
48
+ end
49
+ end
50
+
51
+ protected
52
+ # Based on the fact we dont know what co2_platform
53
+ # we are using how can the options be found!
54
+ #
55
+ # @return [Array<Array<String, String>>] tuples [value, display]
56
+ def get_options(name, filters = [])
57
+ options = {:per_page => per_page}
58
+ options.merge!(:filter_attribute => self.filter_attribute, :filter_value => self.filter_value) if self.filter_attribute && self.filter_value
59
+ response = QuestionChain.calculated_session.generic_objects_for_object_template(name, options)
60
+ options = response.generic_objects.select{|obj| (filters.empty? || (!filters.empty? && filters.include?(obj.id.to_s)))}.map do |obj|
61
+ {:value => obj.send(self.attribute_for_value), :name => obj.send(self.attribute_for_display)}
62
+ end
63
+ options.sort_by{|obj| obj[:name]}
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ module UiObjects
2
+ class ObjectSearch < UiObject
3
+
4
+ # == keys
5
+ key :object_name, String
6
+ key :max_options, Integer, :default => 20
7
+ key :prompt, String
8
+ key :attribute_for_value, String, :default => "id"
9
+ key :attribute_for_display, String, :default => "identifier"
10
+
11
+ # == Validations
12
+ validates_presence_of :object_name
13
+
14
+ # == Hooks
15
+ after_create :add_default_rule
16
+
17
+ # == Attrs
18
+ attr_accessor_with_default :default_rule, true
19
+
20
+ def self.attributes_for_api
21
+ %w(id name _type label position description rules ui_attributes prompt max_options object_name extra_info css_classes)
22
+ end
23
+
24
+ def options(filter, relatable_category_names = [])
25
+ @options ||= get_options(object_name, filter || "", relatable_category_names)
26
+ end
27
+
28
+ protected
29
+ def get_options(object_name, filter, relatable_category_names)
30
+ response = QuestionChain::calculated_session.generic_objects_for_object_template_with_filter(object_name, filter, :relatable_category_values => relatable_category_names, :per_page => per_page)
31
+ response.generic_objects.map do |obj|
32
+ {:value => obj.send(self.attribute_for_value), :name => obj.identifier}
33
+ end
34
+ end
35
+
36
+ def add_default_rule
37
+ if default_rule
38
+ self.rules << Rules::Search.new
39
+ self.save
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module UiObjects
2
+ class RadioButton < UiObject
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module UiObjects
2
+ class RadioGroup < UiObject
3
+
4
+ end
5
+ end
@@ -0,0 +1,70 @@
1
+ module UiObjects
2
+ class RelatableCategoryDropDown < UiObject
3
+
4
+ # == Keys
5
+ key :related_attribute, String
6
+ key :object_name, String
7
+ key :filters, Set
8
+ key :order, String
9
+ key :prompt, String
10
+ key :max_options, Integer, :default => 20
11
+ key :drop_down_target_id, ObjectId
12
+ key :drop_down_target_is_relatable, Boolean, :default => false
13
+ key :populate, Boolean, :default => true
14
+ key :attribute_for_value, String, :default => "id"
15
+ key :attribute_for_display, String, :default => "label"
16
+ key :external, Boolean, :default => true
17
+
18
+ # == Indexes
19
+ ensure_index :filters
20
+ ensure_index :object_name
21
+ ensure_index :related_attribute
22
+
23
+ # == Validations
24
+ validates_presence_of :related_attribute
25
+ validates_presence_of :object_name
26
+
27
+ # == Associations
28
+ belongs_to :drop_down_target, :class_name => "UiObject", :foreign_key => "drop_down_target_id"
29
+
30
+ # == Hooks
31
+ after_create :add_default_rule
32
+
33
+ # == Attrs
34
+ attr_accessor_with_default :default_rule, true
35
+
36
+ def options
37
+ return [] if !populate
38
+ @options ||= relatable_categories.select{|obj| (filters.empty? || (!filters.empty? && filters.include?(obj.id.to_s)))}.map do |obj|
39
+ {:value => obj.id, :name => obj.name}
40
+ end.sort_by{|option| option[:name]}
41
+ end
42
+
43
+ def get_target_drop_down_options(relatable_category_ids = [])
44
+ if drop_down_target_is_relatable
45
+ id = relatable_category_ids.shift
46
+ QuestionChain.calculated_session.related_categories_from_relatable_category(id.to_s, drop_down_target.related_attribute, {:relatable_category_ids => relatable_category_ids, :per_page => per_page})
47
+ else
48
+ QuestionChain.calculated_session.related_objects_from_relatable_categories(self.object_name, relatable_category_ids, :per_page => per_page)
49
+ end
50
+ end
51
+
52
+ def self.attributes_for_api
53
+ %w(id name _type label populate drop_down_target_is_relatable related_attribute order rules filters description drop_down_target_id default_value ui_attributes options prompt max_options object_name order extra_info css_classes)
54
+ end
55
+
56
+ protected
57
+ def relatable_categories(related_attribute = self.related_attribute)
58
+ @relatable_categories ||= QuestionChain.calculated_session.relatable_categories_for_object_template(object_name, related_attribute, :per_page => per_page).relatable_categories
59
+ end
60
+
61
+ # @todo move to after save; check for existing
62
+ def add_default_rule
63
+ if default_rule
64
+ self.rules << Rules::PopulateDropDown.new
65
+ self.save
66
+ end
67
+ end
68
+
69
+ end
70
+ end